Hambatan dalam Berkontribusi Kode: `test::nginx`

API7.ai

November 17, 2022

OpenResty (NGINX + Lua)

Pengujian adalah bagian penting dari pengembangan perangkat lunak. Konsep Test Driven Development (TDD) telah menjadi sangat populer sehingga hampir setiap perusahaan perangkat lunak memiliki tim QA (Quality Assurance) untuk menangani pekerjaan pengujian.

Pengujian adalah landasan kualitas dan reputasi besar OpenResty, tetapi ini juga merupakan bagian yang paling diabaikan dalam proyek open-source OpenResty. Banyak pengembang menggunakan lua-nginx-module setiap hari dan sesekali menjalankan flame graph, tetapi berapa banyak orang yang akan menjalankan kasus uji? Bahkan banyak proyek open-source berbasis OpenResty tidak memiliki kasus uji. Namun, proyek open-source tanpa kasus uji dan integrasi berkelanjutan tidak dapat dipercaya.

Namun, tidak seperti perusahaan komersial, sebagian besar proyek open-source tidak memiliki insinyur pengujian perangkat lunak khusus, jadi bagaimana mereka memastikan kualitas kode mereka? Jawabannya sederhana: "otomatisasi pengujian" dan "integrasi berkelanjutan", dengan poin kunci adalah otomatisasi dan kontinuitas, yang keduanya telah dicapai oleh OpenResty sejauh mungkin.

OpenResty memiliki 70 proyek open-source, dan pengujian unit, pengujian integrasi, pengujian kinerja, pengujian mock, pengujian fuzz, dan beban kerja lainnya sulit diselesaikan secara manual oleh kontributor komunitas. Oleh karena itu, OpenResty berinvestasi lebih banyak dalam pengujian otomatisasi sejak awal. Ini mungkin terlihat memperlambat proyek dalam jangka pendek, tetapi dapat dikatakan bahwa investasi di bidang ini sangat hemat biaya dalam jangka panjang. Jadi, ketika saya berbicara dengan insinyur lain tentang logika dan perangkat pengujian OpenResty, mereka terkesima.

Mari kita bicara tentang filosofi pengujian OpenResty.

Konsep

test::nginx adalah inti dari arsitektur pengujian OpenResty, yang digunakan oleh OpenResty itu sendiri dan pustaka lua-resty di sekitarnya untuk mengatur dan menulis set pengujian. Ini adalah kerangka pengujian dengan ambang batas yang sangat tinggi. Alasannya adalah, tidak seperti kerangka pengujian umum, test::nginx tidak berbasis pada assertion dan tidak menggunakan bahasa Lua, yang mengharuskan pengembang untuk mempelajari dan menggunakan test::nginx dari awal dan membalikkan pengetahuan mereka tentang kerangka pengujian.

Saya mengenal beberapa kontributor OpenResty yang dapat mengirimkan kode C dan Lua ke OpenResty tetapi merasa sulit untuk menulis kasus uji menggunakan test::nginx. Mereka tidak tahu cara menulisnya atau cara memperbaikinya ketika menghadapi kegagalan pengujian. Oleh karena itu, saya menyebut test::nginx sebagai penghalang dalam berkontribusi kode.

test::nginx menggabungkan Perl, data-driven, dan DSL (Domain-specific language). Untuk set kasus uji yang sama, dengan mengontrol parameter dan variabel lingkungan, Anda dapat mencapai efek yang berbeda seperti eksekusi acak, pengulangan berkali-kali, deteksi kebocoran memori, pengujian stres, dll.

Instalasi dan contoh

Sebelum kita menggunakan test::nginx, mari kita pelajari cara menginstalnya.

Untuk instalasi perangkat lunak dalam sistem OpenResty, hanya metode instalasi CI resmi yang paling tepat waktu dan efektif; cara instalasi lainnya selalu menghadapi berbagai masalah. Itulah mengapa saya menyarankan Anda untuk mengambil metode resmi sebagai referensi, di mana Anda dapat menemukan instalasi dan penggunaan test::nginx juga. Ada empat langkah.

  1. Pertama, instal manajer paket Perl cpanminus.
  2. Kemudian, instal test::nginx melalui cpanm.
sudo cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
  1. Selanjutnya, klon kode sumber terbaru.
git clone https://github.com/openresty/test-nginx.git
  1. Terakhir, muat pustaka test-nginx melalui perintah prove Perl dan jalankan set kasus uji di direktori /t.
prove -Itest-nginx/lib -r t

Setelah instalasi, mari kita lihat kasus uji paling sederhana di test::nginx. Kode berikut diadaptasi dari dokumentasi resmi, dan saya telah menghapus semua parameter kontrol yang disesuaikan.

use Test::Nginx::Socket 'no_plan'; run_tests(); __DATA__ === TEST 1: set Server --- config location /foo { echo hi; more_set_headers 'Server: Foo'; } --- request GET /foo --- response_headers Server: Foo --- response_body hi

Meskipun test::nginx ditulis dalam Perl dan berfungsi sebagai salah satu modul, dapatkah Anda melihat sesuatu dalam Perl atau bahasa lain dari pengujian di atas? Benar. Ini karena test::nginx adalah implementasi DSL sendiri oleh penulis dalam Perl, yang dirancang khusus untuk menguji NGINX dan OpenResty.

Jadi, ketika kita pertama kali melihat pengujian semacam ini, kemungkinan besar kita tidak mengerti. Tapi jangan khawatir; mari kita analisis kasus uji di atas.

Pertama-tama, use Test::Nginx::Socket;, yang merupakan cara Perl mereferensikan pustaka, seperti require dalam Lua. Ini juga mengingatkan kita bahwa test::nginx adalah program Perl.

Baris kedua, run_tests(); adalah fungsi Perl dalam test::nginx, fungsi masuk untuk kerangka pengujian. Jika Anda ingin memanggil fungsi Perl lain dalam test::nginx, mereka harus ditempatkan sebelum run_tests untuk menjadi valid.

__DATA__ di baris ketiga adalah penanda yang menunjukkan bahwa semua yang ada di bawahnya adalah data uji, dan fungsi Perl harus diselesaikan sebelum penanda ini.

Selanjutnya === TEST 1: set Server, judul kasus uji, menunjukkan tujuan pengujian ini, dan memiliki alat yang secara otomatis mengatur penomoran di dalamnya.

--- config adalah bidang konfigurasi NGINX. Dalam kasus di atas, kita menggunakan perintah NGINX, bukan Lua, dan jika Anda ingin menambahkan kode Lua, Anda akan melakukannya di sini dengan direktif seperti content_by_lua.

--- request digunakan untuk mensimulasikan terminal untuk mengirim permintaan, diikuti oleh GET /foo, yang menentukan metode dan URI permintaan.

--- response_headers, yang digunakan untuk mendeteksi header respons. Server: Foo berikut menunjukkan header dan value yang harus muncul dalam header respons. Jika tidak, pengujian akan gagal.

Yang terakhir --- response_body, digunakan untuk mendeteksi body respons. hi berikut adalah string yang harus muncul dalam body respons; jika tidak, pengujian akan gagal.

Nah, di sini, kasus uji paling sederhana selesai dianalisis. Jadi, memahami kasus uji adalah prasyarat untuk menyelesaikan pekerjaan pengembangan terkait OpenResty.

Selanjutnya, saatnya masuk ke pengujian langsung. Ingat bagaimana kita menguji server Memcached di artikel terakhir? Benar; kita menggunakan resty untuk mengirim permintaan secara manual, yang diwakili oleh kode berikut.

resty -e 'local memcached = require "resty.memcached" local memc, err = memcached:new() memc:set_timeout(1000) -- 1 sec local ok, err = memc:connect("127.0.0.1", 11212) local ok, err = memc:set("dog", 32) if not ok then ngx.say("failed to set dog: ", err) return end local res, flags, err = memc:get("dog") ngx.say("dog: ", res)'

Tetapi bukankah mengirimnya secara manual tidak cukup cerdas? Jangan khawatir. Kita dapat mencoba mengubah pengujian manual menjadi otomatis setelah mempelajari test::nginx. Misalnya:

use Test::Nginx::Socket::Lua::Stream; run_tests(); __DATA__ === TEST 1: basic get and set --- config location /test { content_by_lua_block { local memcached = require "resty.memcached" local memc, err = memcached:new() if not memc then ngx.say("failed to instantiate memc: ", err) return end memc:set_timeout(1000) -- 1 sec local ok, err = memc:connect("127.0.0.1", 11212) local ok, err = memc:set("dog", 32) if not ok then ngx.say("failed to set dog: ", err) return end local res, flags, err = memc:get("dog") ngx.say("dog: ", res) } } --- stream_config lua_shared_dict memcached 100m; --- stream_server_config listen 11212; content_by_lua_block { local m = require("memcached-server") m.go() } --- request GET /test --- response_body dog: 32 --- no_error_log [error]

Dalam kasus uji ini, saya telah menambahkan --- stream_config, --- stream_server_config, --- no_error_log sebagai item konfigurasi, tetapi pada dasarnya sama, yaitu.

Data dan pengujian dari pengujian dipisahkan untuk membuat keterbacaan dan ekstensibilitas lebih baik dengan mengabstraksi konfigurasi.

Di sinilah test::nginx secara fundamental berbeda dari kerangka pengujian lainnya. DSL ini adalah pedang bermata dua karena membuat logika pengujian jelas dan mudah diperluas. Namun, ini meningkatkan biaya pembelajaran, mengharuskan Anda mempelajari sintaks dan konfigurasi baru sebelum dapat mulai menulis kasus uji.

Ringkasan

test::nginx sangat kuat, tetapi sering kali mungkin tidak selalu cocok untuk skenario Anda. Mengapa memecahkan kupu-kupu dengan roda? Di OpenResty, Anda juga memiliki opsi untuk menggunakan kerangka pengujian berbasis assertion busted. busted yang digabungkan dengan resty menjadi alat baris perintah, dan juga dapat memenuhi banyak kebutuhan pengujian.

Terakhir, saya akan meninggalkan Anda dengan pertanyaan. Bisakah Anda menjalankan pengujian ini untuk Memcached secara lokal? Jika Anda dapat menambahkan kasus uji baru, itu akan sangat bagus.