Sebagai jurutera perisian, anda mungkin biasa menulis kod untuk berinteraksi dengan perkhidmatan HTTP luaran. Lagipun, ia adalah salah satu perkara yang paling biasa kita lakukan! Sama ada mengambil data, memproses pembayaran dengan pembekal atau mengautomasikan siaran media sosial, aplikasi kami hampir selalu melibatkan permintaan HTTP luaran. Untuk memastikan perisian kami boleh dipercayai dan boleh diselenggara, kami memerlukan cara untuk menguji kod yang bertanggungjawab untuk melaksanakan permintaan ini dan mengendalikan ralat yang mungkin berlaku. Ini memberi kita beberapa pilihan:
Pilihan ini tidak begitu buruk, terutamanya jika semuanya boleh digunakan bersama, tetapi kami mempunyai pilihan yang lebih baik: ujian VCR.
Ujian VCR, dinamakan sempena perakam kaset video, ialah sejenis ujian olok-olok yang menjana lekapan ujian daripada permintaan sebenar. Lekapan merekodkan permintaan dan respons untuk digunakan semula secara automatik dalam ujian akan datang. Walaupun anda mungkin perlu mengubah suai lekapan selepas itu untuk mengendalikan input berasaskan masa dinamik atau mengalih keluar bukti kelayakan, ia adalah lebih mudah daripada mencipta olok-olok dari awal. Terdapat beberapa faedah tambahan untuk ujian VCR:
Sekarang anda melihat motivasi di sebalik ujian VCR, mari kita kaji lebih mendalam cara melaksanakannya dalam Go menggunakan dnaeon/go-vcr.
Pustaka ini disepadukan dengan lancar ke dalam mana-mana kod klien HTTP. Jika kod pustaka pelanggan anda belum membenarkan menetapkan *http.Client atau http.Transport Pelanggan, anda harus menambahnya sekarang.
Bagi mereka yang tidak biasa, http.Transport ialah pelaksanaan http.RoundTripper, yang pada asasnya ialah perisian tengah sisi klien yang boleh mengakses permintaan/tindak balas. Ia berguna untuk melaksanakan percubaan semula automatik pada respons 500 tahap atau 429 (had kadar), atau menambah metrik dan mengelog permintaan. Dalam kes ini, ia membenarkan go-vcr untuk mengosongkan semula permintaan ke pelayan HTTP dalam prosesnya sendiri.
Mari kita mulakan dengan contoh mudah. Kami ingin membuat pakej yang membuat permintaan kepada API https://cleanuri.com percuma. Pakej ini akan menyediakan satu fungsi: Shorten(string) (string, error)
Memandangkan ini adalah API percuma, mungkin kita hanya boleh mengujinya dengan membuat permintaan terus ke pelayan? Ini mungkin berkesan, tetapi boleh mengakibatkan beberapa masalah:
Ok, bagaimana jika kita mencipta antara muka dan mengejeknya? Pakej kami sangat mudah, jadi ini akan merumitkannya. Memandangkan perkara peringkat terendah yang kami gunakan ialah *http.Client, kami perlu menentukan antara muka baharu di sekelilingnya dan melaksanakan olok-olok.
Pilihan lain adalah untuk mengatasi URL sasaran untuk menggunakan port tempatan yang disediakan oleh httptest.Server. Ini pada asasnya adalah versi ringkas tentang apa yang dilakukan oleh go-vcr dan akan mencukupi dalam kes mudah kami, tetapi tidak akan dapat diselenggara dalam senario yang lebih kompleks. Malah dalam contoh ini, anda akan melihat cara mengurus lekapan yang dijana adalah lebih mudah daripada mengurus pelaksanaan pelayan olok-olok yang berbeza.
Memandangkan antara muka kami telah ditakrifkan dan kami mengetahui beberapa input/output yang sah daripada mencuba UI di https://cleanuri.com, ini adalah peluang yang baik untuk mempraktikkan pembangunan dipacu ujian. Kami akan mulakan dengan melaksanakan ujian mudah untuk fungsi Shorten kami:
package shortener_test func TestShorten(t *testing.T) { shortened, err := shortener.Shorten("https://dev.to/calvinmclean") if err != nil { t.Errorf("unexpected error: %v", err) } if shortened != "https://cleanuri.com/7nPmQk" { t.Errorf("unexpected result: %v", shortened) } }
Agak mudah! Kami tahu bahawa ujian akan gagal untuk disusun kerana shortener.Shorten tidak ditakrifkan, tetapi kami tetap menjalankannya supaya membetulkannya akan lebih memuaskan.
Akhir sekali, mari kita teruskan dan laksanakan fungsi ini:
package shortener var DefaultClient = http.DefaultClient const address = "https://cleanuri.com/api/v1/shorten" // Shorten will returned the shortened URL func Shorten(targetURL string) (string, error) { resp, err := DefaultClient.PostForm( address, url.Values{"url": []string{targetURL}}, ) if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("unexpected response code: %d", resp.StatusCode) } var respData struct { ResultURL string `json:"result_url"` } err = json.NewDecoder(resp.Body).Decode(&respData) if err != nil { return "", err } return respData.ResultURL, nil }
Kini ujian kami lulus! Ia sama memuaskan seperti yang saya janjikan.
Untuk mula menggunakan VCR, kita perlu memulakan Perakam dan mengatasi shortener.DefaultClient pada permulaan ujian:
func TestShorten(t *testing.T) { r, err := recorder.New("fixtures/dev.to") if err != nil { t.Fatal(err) } defer func() { require.NoError(t, r.Stop()) }() if r.Mode() != recorder.ModeRecordOnce { t.Fatal("Recorder should be in ModeRecordOnce") } shortener.DefaultClient = r.GetDefaultClient() // ...
Jalankan ujian untuk menjana lekapan/dev.to.yaml dengan butiran tentang permintaan dan tindak balas ujian. Apabila kami menjalankan semula ujian, ia menggunakan respons yang direkodkan dan bukannya menghubungi pelayan. Jangan hanya mengambil kata-kata saya untuk itu; matikan WiFi komputer anda dan jalankan semula ujian!
Anda juga mungkin perasan bahawa masa yang diambil untuk menjalankan ujian adalah agak konsisten sejak go-vcr merekod dan memainkan semula tempoh respons. Anda boleh mengubah suai medan ini secara manual dalam YAML untuk mempercepatkan ujian.
Untuk menunjukkan lebih lanjut manfaat ujian jenis ini, mari tambah ciri lain: cuba semula selepas 429 respons kerana pengehadan kadar. Memandangkan kami tahu had kadar API adalah sesaat, Shorten boleh menunggu sebentar secara automatik dan mencuba semula jika ia menerima kod respons 429.
Saya cuba menghasilkan semula ralat ini menggunakan API secara langsung, tetapi nampaknya ia bertindak balas dengan URL sedia ada daripada cache sebelum mempertimbangkan had kadar. Daripada mencemarkan cache dengan URL palsu, kami boleh mencipta olok-olok kami sendiri kali ini.
Ini adalah proses yang mudah kerana kami telah menjana lekapan. Selepas menyalin/menampal lekapan/dev.to.yaml ke fail baharu, salin interaksi permintaan/tindak balas yang berjaya dan tukar kod respons pertama daripada 200 kepada 429. Lekapan ini meniru percubaan semula yang berjaya selepas kegagalan mengehadkan kadar.
Satu-satunya perbezaan antara ujian ini dan ujian asal ialah nama fail lekapan baharu. Output yang dijangkakan adalah sama kerana Shorten harus mengendalikan ralat. Ini bermakna kita boleh membuang ujian dalam satu gelung untuk menjadikannya lebih dinamik:
func TestShorten(t *testing.T) { fixtures := []string{ "fixtures/dev.to", "fixtures/rate_limit", } for _, fixture := range fixtures { t.Run(fixture, func(t *testing.T) { r, err := recorder.New(fixture) if err != nil { t.Fatal(err) } defer func() { require.NoError(t, r.Stop()) }() if r.Mode() != recorder.ModeRecordOnce { t.Fatal("Recorder should be in ModeRecordOnce") } shortener.DefaultClient = r.GetDefaultClient() shortened, err := shortener.Shorten("https://dev.to/calvinmclean") if err != nil { t.Errorf("unexpected error: %v", err) } if shortened != "https://cleanuri.com/7nPmQk" { t.Errorf("unexpected result: %v", shortened) } }) } }
Sekali lagi, ujian baru gagal. Kali ini disebabkan respons 429 yang tidak dikendalikan, jadi mari kita laksanakan ciri baharu untuk lulus ujian. Untuk mengekalkan kesederhanaan, fungsi kami mengendalikan ralat menggunakan masa. Tidur dan panggilan rekursif daripada menangani kerumitan mempertimbangkan percubaan semula maksimum dan pengunduran eksponen:
func Shorten(targetURL string) (string, error) { // ... switch resp.StatusCode { case http.StatusOK: case http.StatusTooManyRequests: time.Sleep(time.Second) return Shorten(targetURL) default: return "", fmt.Errorf("unexpected response code: %d", resp.StatusCode) } // ...
Sekarang jalankan ujian sekali lagi dan lihat mereka lulus!
Ambil langkah lebih jauh sendiri dan cuba tambahkan ujian untuk permintaan buruk, yang akan berlaku apabila menggunakan URL yang tidak sah seperti url-palsu saya.
Kod penuh untuk contoh ini (dan ujian permintaan buruk) tersedia di Github.
Faedah ujian VCR jelas daripada contoh mudah ini, tetapi ia lebih berkesan apabila berurusan dengan aplikasi kompleks yang permintaan dan responsnya sukar digunakan. Daripada berurusan dengan ejekan yang membosankan atau memilih untuk tiada ujian sama sekali, saya menggalakkan anda mencuba ini dalam aplikasi anda sendiri. Jika anda sudah bergantung pada ujian penyepaduan, bermula dengan VCR adalah lebih mudah kerana anda sudah mempunyai permintaan sebenar yang boleh menjana lekapan.
Lihat lebih banyak dokumentasi dan contoh dalam repositori Github pakej: https://github.com/dnaeon/go-vcr
Atas ialah kandungan terperinci Pengujian Pelanggan HTTP yang Mudah dalam Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!