Topik wawancara representatif

Wawancara coding: Bagaimana Anda menguji kode Go yang konkuren dengan testing/synctest?

CodingSulit
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Uji fungsi Go dengan batas waktu (timeout), percobaan ulang latar belakang (background retries), dan context.AfterFunc. Pengujian harus cepat dan dapat diulang tanpa sleep nyata. Jelaskan gelembung testing/synctest, waktu virtual, Wait, semantik pembatalan, dan kasus mana yang masih memerlukan pengujian integrasi nyata.

Petunjuk dan cakupan

Fungsi ini memulai goroutine latar belakang, mencoba ulang dengan exponential backoff, dan mencatat hasil melalui context.AfterFunc saat batas waktu terjadi. Pengujian yang menggunakan time.Sleep berjalan lambat, tidak stabil (flaky), atau meninggalkan pekerjaan di latar belakang. Gunakan Go testing/synctest untuk merancang pengujian deterministik, serta mencakup API eksperimen versus stabil, kebocoran goroutine, I/O nyata, dan batasan clock.

API dan versi harus sesuai dengan rilis Go target. Ini cocok untuk peran coding backend, pustaka konkurensi, dan infrastruktur. Keterampilan intinya adalah pengujian konkuren yang deterministik, sehingga termasuk dalam coding.

Hal yang dinilai oleh pewawancara

Pertama, apakah Anda memahami gelembung (bubble) synctest? Gelembung ini mengisolasi goroutine dan waktu, sementara Wait memungkinkan pekerjaan di dalam gelembung mencapai kondisi diam (quiescence).

Kedua, dapatkah Anda memisahkan waktu virtual dan nyata? API waktu yang digunakan di dalam gelembung dapat dimajukan secara virtual, namun perilaku jaringan nyata, file, atau goroutine eksternal tidak menjadi deterministik secara otomatis.

Ketiga, dapatkah Anda menguji pembatalan dan pembersihan (cleanup)? Pembatalan konteks, callback AfterFunc, dan goroutine percobaan ulang harus selesai atau dihentikan secara eksplisit sebelum pengujian selesai.

Keempat, dapatkah Anda menangani perbedaan versi? Go 1.24 mengekspos synctest secara eksperimental di balik flag; Go 1.25 menyediakan API yang stabil. CI harus mengunci (pin) versinya.

Kelima, dapatkah Anda mempertahankan cakupan integrasi? Pengujian waktu virtual memverifikasi urutan logis, bukan perilaku HTTP, database, penjadwal (scheduler), atau race detector dalam proses nyata.

Pertanyaan untuk diklarifikasi terlebih dahulu

  • Apakah CI menggunakan eksperimen Go 1.24 atau API stabil Go 1.25?
  • Apakah semua goroutine pengujian dibuat di dalam gelembung?
  • Apakah percobaan ulang menggunakan time.After, timer, atau penjadwal eksternal?
  • Setelah pembatalan, apakah I/O saat ini boleh selesai atau harus langsung kembali?
  • Apakah kita memerlukan latensi jaringan nyata, lock database, dan cakupan race?
  • Bisakah kode menerima antarmuka clock atau dependensi, atau apakah fungsi yang ada harus dibungkus?

Kerangka jawaban 30 detik

“Saya akan menjalankan fungsi tersebut di dalam gelembung synctest.Test, memajukan waktu virtual melalui backoff dan tenggat waktu (deadline), serta menggunakan synctest.Wait untuk mencapai quiescence. Saya akan meng-assert jumlah percobaan ulang, error akhir, perilaku satu kali AfterFunc, dan tidak ada pekerjaan setelah pembatalan. Go 1.24 memerlukan flag eksperimen; Go 1.25 memiliki API yang stabil, jadi CI menguncinya. Pemeriksaan HTTP, database, penjadwal, dan race yang nyata dijalankan secara terpisah karena tidak dikontrol secara otomatis oleh gelembung.”

Jawaban langkah demi langkah

Langkah 1: Kunci versi dan API

testing/synctest di Go 1.24 bersifat eksperimental dan memerlukan GOEXPERIMENT=synctest; Go 1.25 mengekspos API standar Test dan Wait. Kunci go.mod, image CI, dan lingkungan command-line agar semantik lokal dan CI cocok.

Langkah 2: Masukkan pekerjaan ke dalam gelembung

Buat setiap goroutine pengujian di dalam gelembung alih-alih memulai pekerjaan yang tidak terkontrol di luar fungsi pengujian. Gelembung harus mengelola pembatalan, penutupan channel, dan pembersihan sumber daya, dan semua tugas harus kembali sebelum gelembung keluar.

go
func TestRetryTimeout(t *testing.T) {
  synctest.Test(t, func(t *testing.T) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    got := runWithRetry(ctx)
    synctest.Wait()
    // Advance virtual time and assert retry/timeout state here.
    _ = got
  })
}

Langkah 3: Majukan waktu virtual

Jangan gunakan time.Sleep nyata untuk backoff. Majukan clock gelembung ke timer berikutnya atau tenggat waktu eksplisit, lalu panggil Wait agar goroutine yang siap dijalankan dapat dieksekusi. Lakukan asersi setelah setiap pemajuan alih-alih melewatkan beberapa batas waktu yang dapat menyembunyikan transisi yang gagal.

Langkah 4: Verifikasi AfterFunc dan pembatalan

context.AfterFunc memulai goroutine callback saat pembatalan terjadi. Catat jumlah callback dan error yang teramati, lalu panggil Wait setelah pembatalan. Jika fungsi mendaftarkan callback berulang kali, lakukan asersi bahwa jalur pembatalan hanya menghasilkan efek samping yang telah dirancang.

Langkah 5: Cakup percobaan ulang dan hasil akhir

Suntikkan urutan kegagalan yang terkontrol, seperti dua kegagalan yang diikuti oleh keberhasilan, dan lakukan asersi pada setiap backoff dan jumlah panggilan. Uji juga kedaluwarsa batas waktu, pembatalan induk, dan kegagalan permanen; pembatalan harus menghentikan percobaan ulang lebih lanjut dan menjaga klasifikasi error tetap stabil.

Langkah 6: Periksa batasan gelembung

Goroutine yang dimulai di luar gelembung, panggilan sistem (system calls), jaringan nyata, dan runtime pihak ketiga mungkin tidak menggunakan waktu virtual. Ganti dependensi tersebut dengan antarmuka atau uji dalam suite integrasi; determinisme gelembung bukanlah jaminan end-to-end.

Langkah 7: Gabungkan alat verifikasi lainnya

Synctest mencakup timing logis. go test -race memeriksa data race, sementara pengujian HTTP dan database nyata memeriksa koneksi, tenggat waktu, dan perilaku protokol. Lacak durasi pengujian, batas percobaan ulang, dan konvergensi tugas agar pengujian tidak bocor atau melambat secara diam-diam.

Contoh jawaban

“Pertama-tama saya akan mengunci versi Go. Synctest Go 1.24 membutuhkan flag eksperimen; Go 1.25 menggunakan synctest.Test dan synctest.Wait yang stabil. Saya akan membuat semua goroutine pengujian di dalam gelembung, menyuntikkan urutan kegagalan, memajukan waktu virtual melalui backoff dan batas waktu, menunggu quiescence setelah setiap pemajuan, dan meng-assert panggilan, error, callback, serta pembersihan pembatalan.

Saya tidak akan menggunakan sleep nyata atau memperlakukan goroutine jaringan, database, atau pihak ketiga di luar gelembung sebagai pekerjaan waktu virtual. Jalur-jalur tersebut mendapatkan fake antarmuka untuk pengujian logika serta integrasi nyata ditambah go test -race untuk protokol, lock, dan race. Pengujian harus membuktikan bahwa tidak ada tugas gelembung yang tersisa saat keluar.”

Kesalahan umum

  • Mempertahankan time.Sleep di dalam gelembung → pengujian tetap lambat dan tidak deterministik → majukan waktu virtual.
  • Hanya meng-assert hasil akhir → percobaan ulang dan pembersihan pembatalan bisa salah → assert timer, panggilan, dan callback langkah demi langkah.
  • Memulai goroutine di luar gelembung → konvergensi tidak dapat ditunggu → buat gelembung memiliki kontrol penuh atas pekerjaan tersebut.
  • Memperlakukan jaringan nyata sebagai waktu virtual → I/O tetap tidak stabil (flaky) → gunakan fake dan pengujian integrasi terpisah.
  • Mengabaikan perbedaan API 1.24/1.25 → build lokal dan CI berbeda → kunci Go dan flag eksperimen.
  • Melewatkan race detector → timing bisa benar sementara data race tetap ada → gabungkan go test -race.
  • Mencoba ulang setelah pembatalan → permintaan dan penggunaan sumber daya membengkak → periksa konteks sebelum setiap penantian dan panggilan.
  • Menggunakan satu Wait akhir untuk semua asersi → batas kegagalan menjadi tidak jelas → majukan dan verifikasi secara bertahap.

Pertanyaan lanjutan

Pertanyaan lanjutan 1: Apakah synctest menggantikan pengujian real-time?

Tidak. Ini memverifikasi logika konkuren yang terkontrol dan hubungan temporal; clock, jaringan, database, dan penjadwal masih memerlukan pengujian lingkungan nyata.

Pertanyaan lanjutan 2: Mengapa memanggil Wait?

Memajukan waktu virtual membuat timer kedaluwarsa; Wait menjalankan goroutine yang siap berjalan di dalam gelembung hingga mencapai quiescence, membuat asersi stabil.

Pertanyaan lanjutan 3: Bagaimana Anda menguji pemblokiran permanen?

Beri operasi tersebut batas waktu (deadline), majukan waktu ke titik tersebut, dan lakukan asersi pada error serta pembersihan. Untuk pemblokiran eksternal yang tidak dapat dibatalkan, gunakan fake atau pengujian timeout yang terisolasi daripada menunggu selamanya di dalam gelembung.

Pertanyaan lanjutan 4: Bisakah eksperimen Go 1.24 digunakan di produksi?

Ini dapat mendukung pengujian yang terkontrol, tetapi status eksperimen, flag, dan risiko peningkatan harus dinyatakan secara eksplisit. Untuk kompatibilitas yang stabil, pilih API Go 1.25 dan kunci di CI.

Pertanyaan lanjutan 5: Bagaimana Anda menemukan kebocoran di luar gelembung?

Periksa sinyal penyelesaian dan sumber daya yang ditutup sebelum keluar, lalu gabungkan -race, profil goroutine, atau timeout integrasi. Synctest bukanlah pendeteksi kebocoran tingkat proses yang lengkap.

Pertanyaan lanjutan 6: Bagaimana Anda mendeteksi backoff drift?

Catat timestamp virtual setiap panggilan dan bandingkan dengan urutan yang diharapkan. Uji juga pemotongan batas waktu pada penantian akhir dan pembatalan dini.

Sumber publik

Pertanyaan terkait

Alat wawancara terkait

Gunakan Tangkapan Layar untuk perintah coding

Ambil tangkapan layar soal, lalu telusuri batasan, solusi, kode, edge case, dan kompleksitas secara berurutan.

Lihat alat