Penggunaan `test::nginx` yang Jarang Diketahui

API7.ai

November 24, 2022

OpenResty (NGINX + Lua)

Dalam dua artikel sebelumnya, Anda telah menguasai sebagian besar penggunaan test::nginx, dan saya yakin Anda dapat memahami sebagian besar set kasus uji dalam proyek OpenResty. Ini lebih dari cukup untuk mempelajari OpenResty dan perpustakaan sekitarnya.

Namun, jika Anda tertarik untuk menjadi kontributor kode OpenResty, atau jika Anda menggunakan test::nginx untuk menulis kasus uji dalam proyek Anda, maka Anda perlu mempelajari beberapa penggunaan yang lebih canggih dan kompleks.

Artikel hari ini mungkin akan menjadi bagian yang paling "tidak populer" dari seri ini karena ini adalah sesuatu yang belum pernah dibagikan oleh siapa pun sebelumnya. Ambil contoh lua-nginx-module, modul inti dalam OpenResty, yang memiliki lebih dari 70 kontributor di seluruh dunia, tetapi tidak setiap kontributor pernah menulis kasus uji. Jadi, jika Anda membaca artikel hari ini, pemahaman Anda tentang test::nginx akan masuk ke dalam 100 besar di seluruh dunia.

Debugging dalam tes

Pertama, mari kita lihat beberapa bagian yang paling sederhana dan paling sering digunakan oleh pengembang dalam debugging normal. Di sini, kita akan memperkenalkan skenario penggunaan bagian-bagian yang terkait dengan debugging satu per satu.

ONLY

Seringkali, kita menambahkan kasus uji baru ke set kasus uji yang sudah ada. Jika file tes berisi banyak kasus uji, menjalankannya akan memakan waktu, terutama ketika Anda perlu memodifikasi kasus uji berulang kali.

Jadi, apakah ada cara untuk hanya menjalankan satu kasus uji yang Anda tentukan? Ini dapat dengan mudah dilakukan dengan bagian ONLY.

=== TEST 1: sanity === TEST 2: get --- ONLY

Pseudocode di atas menunjukkan cara menggunakan bagian ini. Dengan menempatkan --- ONLY di baris terakhir dari kasus uji yang perlu dijalankan sendiri, maka ketika Anda menggunakan prove untuk menjalankan file kasus uji, semua kasus uji lainnya akan diabaikan, dan hanya tes ini yang akan dijalankan.

Namun, ini hanya sesuai ketika Anda sedang melakukan debugging. Jadi, ketika perintah prove menemukan bagian ONLY, itu juga akan mengingatkan Anda untuk tidak lupa menghapusnya ketika Anda mengirimkan kode Anda.

SKIP

Kebutuhan yang sesuai dengan menjalankan hanya satu kasus uji adalah mengabaikan kasus uji tertentu. Bagian SKIP, yang biasanya digunakan untuk menguji fungsionalitas yang belum diimplementasikan:

=== TEST 1: sanity === TEST 2: get --- SKIP

Seperti yang dapat Anda lihat dari pseudocode ini, penggunaannya mirip dengan ONLY. Karena kita melakukan pengembangan berbasis tes, kita perlu menulis kasus uji terlebih dahulu; dan ketika kita sedang mengkodekan implementasi secara kolektif, kita mungkin perlu menunda implementasi suatu fitur karena kesulitan atau prioritas implementasi. Kemudian, Anda dapat melewati set kasus uji yang sesuai terlebih dahulu, dan kemudian menghapus bagian SKIP ketika implementasi selesai.

LAST

Bagian umum lainnya adalah LAST, yang juga sederhana untuk digunakan, karena kasus uji sebelumnya akan dieksekusi dan yang setelahnya akan diabaikan.

=== TEST 1: sanity === TEST 2: get --- LAST === TEST 3: set

Anda mungkin bertanya-tanya, saya dapat memahami pentingnya ONLY dan SKIP, tetapi apa gunanya LAST? Sebenarnya, terkadang kasus uji Anda memiliki ketergantungan, dan Anda perlu menjalankan beberapa kasus uji pertama sebelum tes berikutnya masuk akal. Jadi, dalam hal ini, LAST sangat berguna ketika Anda melanjutkan debugging.

plan

Dari semua fungsi test::nginx, plan adalah salah satu yang paling menjengkelkan dan sulit dipahami. Ini berasal dari modul Perl Test::Plan, dokumentasinya tidak ada di test::nginx, dan menemukan penjelasannya tidak mudah. Oleh karena itu, saya akan memperkenalkannya di bagian awal. Saya telah melihat beberapa kontributor kode OpenResty yang terjebak dalam lubang ini dan bahkan tidak bisa keluar.

Berikut adalah contoh konfigurasi serupa yang dapat Anda lihat di awal setiap file dalam set tes resmi OpenResty:

plan tests => repeat_each() * (3 * blocks());

Arti dari plan di sini adalah berapa banyak tes yang harus dilakukan sesuai rencana dalam seluruh file tes. Jika hasil dari jalannya akhir tidak sesuai dengan rencana, tes akan gagal.

Untuk contoh ini, jika nilai repeat_each adalah 2 dan ada 10 kasus uji, maka nilai plan seharusnya 2 x 3 x 10 = 60. Satu-satunya hal yang mungkin membuat Anda bingung adalah arti dari angka 3, yang terlihat seperti angka ajaib!

Jangan khawatir, mari kita lanjutkan melihat contoh, Anda akan bisa memahaminya dalam sekejap. Pertama, bisakah Anda mengetahui nilai plan yang benar dalam kasus uji berikut?

=== TEST 1: sanity --- config location /t { content_by_lua_block { ngx.say("hello") } } --- request GET /t --- response_body hello

Saya yakin semua orang akan menyimpulkan bahwa plan = 1, karena tes hanya memeriksa response_body.

Tapi itu tidak benar! Jawaban yang benar adalah plan = 2. Mengapa? Karena test::nginx memiliki pemeriksaan implisit, yaitu --- error_code: 200, yang mendeteksi apakah kode respons HTTP adalah 200 secara default.

Jadi, angka ajaib 3 di atas sebenarnya berarti bahwa setiap tes diperiksa secara eksplisit dua kali, misalnya untuk body dan error log, dan secara implisit untuk response code.

Karena ini sangat rentan kesalahan, saya sarankan Anda mematikan plan dengan menggunakan metode berikut.

use Test::Nginx::Socket 'no_plan';

Jika Anda tidak bisa mematikannya, misalnya, jika Anda menemukan plan yang tidak akurat dalam set tes resmi OpenResty, disarankan untuk tidak menyelidiki penyebabnya, tetapi cukup menambah atau mengurangi angka pada ekspresi plan.

plan tests => repeat_each() * (3 * blocks()) + 2;

Ini juga metode resmi yang akan digunakan.

Preprocessor

Kita tahu bahwa mungkin ada beberapa pengaturan publik antara kasus uji yang berbeda dalam file tes yang sama. Jika pengaturan diulang dalam setiap kasus uji, itu akan membuat kode menjadi berlebihan dan merepotkan untuk dimodifikasi nanti.

Pada titik ini, Anda dapat menggunakan direktif add_block_preprocessor untuk menambahkan sepotong kode Perl, seperti berikut:

add_block_preprocessor(sub { my $block = shift; if (!defined $block->config) { $block->set_value("config", <<'_END_'); location = /t { echo $arg_a; } _END_ } });

Preprocessor ini menambahkan bagian config ke semua kasus uji, dan kontennya adalah location /t, sehingga dalam kasus uji Anda nanti, Anda dapat menghilangkan config dan mengaksesnya langsung.

=== TEST 1: --- request GET /t?a=3 --- response_body 3 === TEST 2: --- request GET /t?a=blah --- response_body blah

Fungsi Kustom

Selain menambahkan kode Perl ke preprocessor, Anda juga dapat menambahkan fungsi Perl secara sewenang-wenang, atau fungsi kustom seperti yang kita sebut, sebelum fungsi run_tests.

Berikut adalah contoh yang menambahkan fungsi yang membaca file dan menggabungkannya dengan direktif eval untuk mengimplementasikan POST file:

sub read_file { my $infile = shift; open my $in, $infile or die "cannot open $infile for reading: $!"; my $content = do { local $/; <$in> }; close $in; $content; } our $CONTENT = read_file("t/test.jpg"); run_tests; __DATA__ === TEST 1: sanity --- request eval "POST /\n$::CONTENT"

Shuffle

Selain di atas, test::nginx memiliki lubang yang kurang dikenal: ia mengeksekusi kasus uji secara acak secara default, alih-alih mengikuti urutan dan penomoran kasus uji.

Awalnya dimaksudkan untuk menguji lebih banyak masalah. Bagaimanapun, setelah setiap kasus uji dijalankan, proses NGINX ditutup, dan proses NGINX baru dimulai untuk mengeksekusinya, sehingga hasilnya seharusnya tidak terkait dengan urutan.

Untuk proyek tingkat dasar, ini benar. Namun, untuk proyek tingkat aplikasi, penyimpanan persisten seperti database ada di luar. Eksekusi yang sembarangan dapat menyebabkan hasil yang salah. Karena itu acak setiap kali, mungkin atau mungkin tidak melaporkan kesalahan, dan kesalahan mungkin berbeda setiap kali. Ini jelas menyebabkan kebingungan bagi pengembang, termasuk saya, karena saya telah terjebak di sini berkali-kali.

Jadi, saran saya adalah: silakan matikan fitur ini. Anda dapat mematikannya dengan dua baris kode berikut:

no_shuffle(); run_tests;

Khususnya, no_shuffle digunakan untuk menonaktifkan pengacakan dan memungkinkan tes berjalan secara ketat sesuai urutan kasus uji.

reindex

Set kasus uji OpenResty memiliki persyaratan format yang ketat. Setiap kasus uji perlu dipisahkan oleh tiga baris baru, dan penomoran kasus uji harus benar-benar bertambah sendiri.

Untungnya, kita memiliki alat otomatis, reindex, untuk melakukan hal-hal yang membosankan ini, yang tersembunyi dalam proyek openresty-devel-utils. Karena tidak ada dokumentasi tentangnya, hanya sedikit orang yang mengetahuinya.

Jika Anda tertarik, Anda dapat mencoba mengacak penomoran kasus uji, atau menambah atau mengurangi jumlah baris baru, dan kemudian menggunakan alat ini untuk menyusunnya dan melihat apakah Anda dapat mengembalikannya.

Ringkasan

Ini adalah akhir dari pengenalan test::nginx. Tentu saja, ada lebih banyak fungsi, kita hanya membahas yang inti dan paling penting. "Beri seseorang ikan dan Anda memberinya makan untuk sehari; ajari dia cara memancing dan Anda memberinya makan seumur hidup." Saya telah mengajari Anda metode dasar dan tindakan pencegahan untuk mempelajari pengujian, maka Anda dapat menggali lebih dalam ke set kasus uji resmi untuk pemahaman yang lebih baik.

Terakhir, silakan pikirkan pertanyaan di bawah ini. Apakah ada tes dalam pengembangan proyek Anda? Dan kerangka apa yang Anda gunakan untuk menguji? Silakan bagikan artikel ini dengan lebih banyak orang untuk bertukar dan belajar bersama.