Pengujian Permintaan HTTP: Alat Bertahan Hidup Pengembang

Diterbitkan: 2022-03-11

Apa yang Harus Dilakukan Ketika Suite Pengujian Tidak Layak?

Ada kalanya kami – programmer dan/atau klien kami – memiliki sumber daya yang terbatas untuk menulis hasil yang diharapkan dan pengujian otomatis untuk hasil tersebut. Ketika aplikasi cukup kecil, Anda dapat mengambil jalan pintas dan melewatkan tes karena Anda ingat (kebanyakan) apa yang terjadi di tempat lain dalam kode saat Anda menambahkan fitur, memperbaiki bug, atau refactor. Meskipun demikian, kami tidak akan selalu bekerja dengan aplikasi kecil, ditambah lagi, mereka cenderung menjadi lebih besar dan lebih kompleks dari waktu ke waktu. Ini membuat pengujian manual menjadi sulit dan sangat mengganggu.

Untuk beberapa proyek terakhir saya, saya dipaksa untuk bekerja tanpa pengujian otomatis dan sejujurnya, sangat memalukan jika klien mengirim email kepada saya setelah kode push untuk mengatakan bahwa aplikasi tersebut rusak di tempat-tempat di mana saya bahkan belum menyentuh kodenya.

Jadi, dalam kasus di mana klien saya tidak memiliki anggaran atau niat untuk menambahkan kerangka pengujian otomatis, saya mulai menguji fungsionalitas dasar seluruh situs web dengan mengirimkan permintaan HTTP ke setiap halaman individual, mengurai header respons dan mencari '200' tanggapan. Kedengarannya sederhana dan sederhana, tetapi ada banyak hal yang dapat Anda lakukan untuk memastikan kesetiaan tanpa benar-benar harus menulis tes, unit, fungsional, atau integrasi apa pun.

Pengujian Otomatis

Dalam pengembangan web, pengujian otomatis terdiri dari tiga jenis pengujian utama: pengujian unit, pengujian fungsional, dan pengujian integrasi. Kami sering menggabungkan pengujian unit dengan pengujian fungsional dan integrasi untuk memastikan semuanya berjalan lancar sebagai keseluruhan aplikasi. Ketika tes ini dijalankan secara serempak, atau berurutan (sebaiknya dengan satu perintah atau klik), kami mulai menyebutnya tes otomatis, unit atau tidak.

Sebagian besar tujuan dari tes ini (setidaknya di web dev) adalah untuk memastikan semua halaman aplikasi dirender tanpa masalah, bebas dari kesalahan fatal (penghentian aplikasi) atau bug.

Pengujian Unit

Pengujian unit adalah proses pengembangan perangkat lunak di mana bagian terkecil dari kode – unit – diuji secara independen untuk operasi yang benar. Berikut ini contoh di Ruby:

 test “should return active users” do active_user = create(:user, active: true) non_active_user = create(:user, active: false) result = User.active assert_equal result, [active_user] end

Pengujian Fungsional

Pengujian fungsional adalah teknik yang digunakan untuk memeriksa fitur dan fungsionalitas sistem atau perangkat lunak, yang dirancang untuk mencakup semua skenario interaksi pengguna, termasuk jalur kegagalan dan kasus batas.

Catatan: semua contoh kami ada di Ruby.

 test "should get index" do get :index assert_response :success assert_not_nil assigns(:object) end

Tes integrasi

Setelah modul diuji unit, mereka diintegrasikan satu per satu, berurutan, untuk memeriksa perilaku kombinasional, dan untuk memvalidasi bahwa persyaratan diterapkan dengan benar.

 test "login and browse site" do # login via https https! get "/login" assert_response :success post_via_redirect "/login", username: users(:david).username, password: users(:david).password assert_equal '/welcome', path assert_equal 'Welcome david!', flash[:notice] https!(false) get "/articles/all" assert_response :success assert assigns(:articles) end

Ujian di Dunia Ideal

Pengujian diterima secara luas di industri dan masuk akal; tes yang baik memungkinkan Anda:

  • Kualitas menjamin seluruh aplikasi Anda dengan sedikit usaha manusia
  • Identifikasi bug lebih mudah karena Anda tahu persis di mana kode Anda rusak dari kegagalan pengujian
  • Buat dokumentasi otomatis untuk kode Anda
  • Hindari 'coding sembelit', yang menurut beberapa orang di Stack Overflow, adalah cara lucu untuk mengatakan, “ketika Anda tidak tahu apa yang harus ditulis selanjutnya, atau Anda memiliki tugas berat di depan Anda, mulailah dengan menulis kecil .”

Saya bisa terus dan terus tentang betapa hebatnya ujian itu, dan bagaimana ujian itu mengubah dunia dan yada yada yada, tetapi Anda mengerti maksudnya. Secara konseptual, tes itu luar biasa.

Terkait: Tes Unit, Cara Menulis Kode yang Dapat Diuji dan Mengapa Itu Penting

Ujian di Dunia Nyata

Meskipun ada manfaat untuk ketiga jenis pengujian, mereka tidak ditulis di sebagian besar proyek. Mengapa? Baiklah, izinkan saya menguraikannya:

Waktu/Batas Waktu

Setiap orang memiliki tenggat waktu, dan menulis tes baru dapat menghalangi untuk memenuhinya. Diperlukan waktu setengah (atau lebih) untuk menulis aplikasi dan pengujiannya masing-masing. Sekarang, beberapa dari Anda tidak setuju dengan ini, dengan alasan menghemat waktu pada akhirnya, tetapi saya tidak berpikir demikian dan saya akan menjelaskan alasannya di 'Perbedaan Pendapat'.

Masalah Klien

Seringkali, klien tidak benar-benar memahami apa itu pengujian, atau mengapa pengujian memiliki nilai untuk aplikasi. Klien cenderung lebih peduli dengan pengiriman produk yang cepat dan oleh karena itu melihat pengujian terprogram sebagai kontraproduktif.

Atau, mungkin sesederhana klien yang tidak memiliki anggaran untuk membayar waktu tambahan yang diperlukan untuk mengimplementasikan tes ini.

Kurangnya Pengetahuan

Ada suku pengembang yang cukup besar di dunia nyata yang tidak mengetahui adanya pengujian. Di setiap konferensi, pertemuan, konser, (bahkan dalam mimpi saya), saya bertemu pengembang yang tidak tahu cara menulis tes, tidak tahu apa yang harus diuji, tidak tahu cara menyiapkan kerangka kerja untuk pengujian, dan sebagainya di. Pengujian tidak diajarkan secara pasti di sekolah, dan mungkin merepotkan untuk menyiapkan/mempelajari kerangka kerja agar dapat berjalan. Jadi ya, ada hambatan pasti untuk masuk.

'Ini Banyak Pekerjaan'

Tes menulis bisa sangat melelahkan bagi programmer baru dan berpengalaman, bahkan untuk tipe jenius yang mengubah dunia, dan yang terpenting, tes menulis tidak menarik. Orang mungkin berpikir, “Mengapa saya harus terlibat dalam pekerjaan yang tidak menyenangkan ketika saya dapat menerapkan fitur utama dengan hasil yang akan mengesankan klien saya?” Ini argumen yang sulit.

Terakhir, namun tidak kalah pentingnya, sulit untuk menulis tes dan siswa ilmu komputer tidak dilatih untuk itu.

Oh, dan refactoring dengan unit test tidak menyenangkan.

Perbedaan Pendapat

Menurut pendapat saya, pengujian unit masuk akal untuk logika algoritmik tetapi tidak begitu banyak untuk mengoordinasikan kode hidup.

Orang-orang mengklaim bahwa meskipun Anda menginvestasikan waktu ekstra di awal untuk menulis tes, itu menghemat berjam-jam kemudian saat men-debug atau mengubah kode. Saya mohon untuk berbeda dan menawarkan satu pertanyaan: Apakah kode Anda statis, atau selalu berubah?

Bagi kebanyakan dari kita, itu selalu berubah. Jika Anda menulis perangkat lunak yang sukses, Anda selalu menambahkan fitur, mengubah yang sudah ada, menghapusnya, memakannya, apa pun, dan untuk mengakomodasi perubahan ini, Anda harus terus mengubah pengujian, dan mengubah pengujian membutuhkan waktu.

Tapi, Anda Perlu Beberapa Jenis Pengujian

Tidak ada yang akan berargumen bahwa kekurangan pengujian apa pun adalah kemungkinan terburuk. Setelah membuat perubahan pada kode Anda, Anda perlu mengonfirmasi bahwa itu benar-benar berfungsi. Banyak programmer mencoba menguji dasar-dasarnya secara manual: Apakah rendering halaman di browser? Apakah formulir sudah diserahkan? Apakah konten yang ditampilkan benar? Dan seterusnya, tapi menurut saya, ini barbar, tidak efisien dan padat karya.

Apa yang Saya Gunakan Sebagai gantinya

Tujuan pengujian aplikasi web, baik secara manual atau otomatis, adalah untuk mengonfirmasi bahwa halaman tertentu dirender di browser pengguna tanpa kesalahan fatal, dan menampilkan kontennya dengan benar. Salah satu cara (dan dalam banyak kasus, cara yang lebih mudah) untuk mencapai ini adalah dengan mengirimkan permintaan HTTP ke titik akhir aplikasi dan menguraikan respons. Kode respons memberi tahu Anda apakah halaman berhasil dikirim. Sangat mudah untuk menguji konten dengan mem-parsing badan respons dari permintaan HTTP dan mencari kecocokan string teks tertentu, atau, Anda bisa menjadi lebih menarik dan menggunakan pustaka web scraping seperti nokogiri.

Jika beberapa titik akhir memerlukan login pengguna, Anda dapat menggunakan perpustakaan yang dirancang untuk mengotomatisasi interaksi (ideal saat melakukan tes integrasi) seperti mekanisasi untuk login atau mengklik tautan tertentu. Sungguh, dalam gambaran besar pengujian otomatis, ini sangat mirip dengan integrasi atau pengujian fungsional (tergantung bagaimana Anda menggunakannya), tetapi jauh lebih cepat untuk menulis dan dapat dimasukkan dalam proyek yang sudah ada, atau ditambahkan ke yang baru , dengan sedikit usaha daripada menyiapkan seluruh kerangka pengujian. tepat!

Terkait: Pekerjakan 3% insinyur QA lepas teratas.

Kasus tepi menghadirkan masalah lain ketika berhadapan dengan basis data besar dengan rentang nilai yang luas; menguji apakah aplikasi kita bekerja dengan lancar di semua kumpulan data yang diantisipasi dapat menjadi hal yang menakutkan.

Salah satu cara untuk melakukannya adalah dengan mengantisipasi semua kasus tepi (yang tidak hanya sulit, sering kali tidak mungkin) dan menulis tes untuk masing-masing kasus. Ini bisa dengan mudah menjadi ratusan baris kode (bayangkan kengeriannya) dan rumit untuk dipelihara. Namun, dengan permintaan HTTP dan hanya satu baris kode, Anda dapat menguji kasus tepi tersebut secara langsung pada data dari produksi, diunduh secara lokal di mesin pengembangan Anda, atau di server pementasan.

Sekarang tentu saja, teknik pengujian ini bukanlah peluru perak dan memiliki banyak kekurangan, sama seperti metode lainnya, tetapi menurut saya jenis pengujian ini lebih cepat, dan lebih mudah, untuk ditulis dan dimodifikasi.

Dalam Praktik: Menguji dengan permintaan HTTP

Karena kami telah menetapkan bahwa menulis kode tanpa jenis pengujian apa pun yang menyertai bukanlah ide yang baik, pengujian dasar saya untuk seluruh aplikasi adalah mengirim permintaan HTTP ke semua halamannya secara lokal dan mengurai header respons untuk a 200 (atau diinginkan) kode.

Misalnya, jika kita menulis tes di atas (yang mencari konten tertentu dan kesalahan fatal) dengan permintaan HTTP sebagai gantinya (di Ruby), itu akan menjadi seperti ini:

 # testing for fatal error http_code = `curl -X #{route[:method]} -s -o /dev/null -w "%{http_code}" #{Rails.application.routes.url_helpers.articles_url(host: 'localhost', port: 3000) }` if http_code !~ /200/ return “articles_url returned with #{http_code} http code.” end # testing for content active_user = create(:user, name: “user1”, active: true) non_active_user = create(:user, name: “user2”, active: false) content = `curl #{Rails.application.routes.url_helpers.active_user_url(host: 'localhost', port: 3000) }` if content !~ /#{active_user.name}/ return “Content mismatch active user #{active_user.name} not found in text body” #You can customise message to your liking end if content =~ /#{non_active_user.name}/ return “Content mismatch non active user #{active_user.name} found in text body” #You can customise message to your liking end

Baris curl -X #{route[:method]} -s -o /dev/null -w "%{http_code}" #{Rails.application.routes.url_helpers.articles_url(host: 'localhost', port: 3000) } mencakup banyak kasus uji; metode apa pun yang memunculkan kesalahan pada halaman artikel akan ditangkap di sini, sehingga secara efektif mencakup ratusan baris kode dalam satu pengujian.

Bagian kedua, yang menangkap kesalahan konten secara khusus, dapat digunakan beberapa kali untuk memeriksa konten pada suatu halaman. (Permintaan yang lebih kompleks dapat ditangani menggunakan mechanize , tetapi itu di luar cakupan blog ini.)

Sekarang, jika Anda ingin menguji apakah halaman tertentu berfungsi pada kumpulan nilai database yang besar dan bervariasi (misalnya, templat halaman artikel Anda berfungsi untuk semua artikel dalam database produksi), Anda dapat melakukan:

 ids = Article.all.select { |post| `curl -s -o /dev/null -w “%{http_code}” #{Rails.application.routes.url_helpers.article_url(post, host: 'localhost', port: 3000) }`.to_i != 200).map(&:id) return ids

Ini akan mengembalikan larik ID dari semua artikel dalam database yang tidak dirender, jadi sekarang Anda dapat secara manual membuka halaman artikel tertentu dan memeriksa masalahnya.

Sekarang, saya mengerti bahwa cara pengujian ini mungkin tidak berfungsi dalam kasus tertentu, seperti menguji skrip mandiri atau mengirim email, dan tidak dapat disangkal lebih lambat daripada pengujian unit karena kami membuat panggilan langsung ke titik akhir untuk setiap pengujian, tetapi ketika Anda tidak dapat memiliki tes unit, atau tes fungsional, atau keduanya, ini lebih baik daripada tidak sama sekali.

Bagaimana Anda akan menyusun tes ini? Dengan proyek kecil yang tidak rumit, Anda dapat menulis semua pengujian dalam satu file dan menjalankan file tersebut setiap kali sebelum Anda melakukan perubahan, tetapi sebagian besar proyek akan memerlukan serangkaian pengujian.

Saya biasanya menulis dua hingga tiga tes per titik akhir, tergantung pada apa yang saya uji. Anda juga dapat mencoba menguji konten individual (mirip dengan pengujian unit), tetapi saya pikir itu akan berlebihan dan lambat karena Anda akan membuat panggilan HTTP untuk setiap unit. Tapi, di sisi lain, mereka akan lebih bersih dan mudah dimengerti.

Saya sarankan untuk meletakkan pengujian Anda di folder pengujian reguler Anda dengan setiap titik akhir utama memiliki filenya sendiri (di Rails, misalnya, setiap model/pengontrol masing-masing akan memiliki satu file), dan file ini dapat dibagi menjadi tiga bagian sesuai dengan apa yang kami sedang menguji. Saya sering memiliki setidaknya tiga tes:

Tes Satu

Periksa apakah halaman kembali tanpa kesalahan fatal.

Uji satu memeriksa bahwa halaman kembali tanpa kesalahan fatal.

Perhatikan bagaimana saya membuat daftar semua titik akhir untuk Post dan mengulanginya untuk memeriksa bahwa setiap halaman dirender tanpa kesalahan. Dengan asumsi semuanya berjalan dengan baik, dan semua halaman telah dirender, Anda akan melihat sesuatu seperti ini di terminal: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of failed url(s) -- []

Jika ada halaman yang tidak dirender, Anda akan melihat sesuatu seperti ini (dalam contoh ini, halaman posts/index page memiliki kesalahan dan karenanya tidak dirender): ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of failed url(s) -- [{:url=>”posts_url”, :params=>[], :method=>”GET”, :http_code=>”500”}]

Tes Dua

Konfirmasikan bahwa semua konten yang diharapkan ada di sana:

Uji dua mengonfirmasi bahwa semua konten yang diharapkan ada di sana.

Jika semua konten yang kami harapkan ditemukan di halaman, hasilnya akan terlihat seperti ini (dalam contoh ini kami memastikan posts/:id memiliki judul, deskripsi, dan status posting): ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of content(s) not found on Post#show page with post id: 1 -- []

Jika konten yang diharapkan tidak ditemukan pada halaman (di sini kami mengharapkan halaman untuk menampilkan status posting - 'Aktif' jika posting aktif, 'Nonaktif' jika posting dinonaktifkan) hasilnya terlihat seperti ini: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of content(s) not found on Post#show page with post id: 1 -- [“Active”]

Tes Tiga

Periksa apakah halaman dirender di semua set data (jika ada):

Uji 3 memeriksa bahwa halaman dirender di semua set data.

Jika semua halaman dirender tanpa kesalahan, kita akan mendapatkan daftar kosong: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of post(s) with error in rendering -- []

Jika konten dari beberapa catatan mengalami masalah rendering (dalam contoh ini, halaman dengan ID 2 dan 5 memberikan kesalahan) hasilnya terlihat seperti ini: ➜ sample_app git:(master) ✗ ruby test/http_request/post_test.rb List of post(s) with error on rendering -- [2,5]

Jika Anda ingin mengutak-atik kode demonstrasi di atas, inilah proyek github saya.

Jadi Mana yang Lebih Baik? Tergantung…

Pengujian Permintaan HTTP mungkin merupakan pilihan terbaik Anda jika:

  • Anda sedang bekerja dengan aplikasi web
  • Anda berada dalam krisis waktu dan ingin menulis sesuatu dengan cepat
  • Anda sedang bekerja dengan proyek besar, proyek yang sudah ada sebelumnya di mana tes tidak ditulis, tetapi Anda masih ingin beberapa cara untuk memeriksa kode
  • Kode Anda melibatkan permintaan dan tanggapan sederhana
  • Anda tidak ingin menghabiskan sebagian besar waktu Anda untuk mempertahankan tes (saya membaca di suatu tempat unit test = neraka pemeliharaan, dan saya sebagian setuju dengannya)
  • Anda ingin menguji apakah aplikasi bekerja di semua nilai dalam database yang ada

Pengujian tradisional sangat ideal ketika:

  • Anda berurusan dengan sesuatu selain aplikasi web, seperti skrip
  • Anda sedang menulis kode algoritmik yang kompleks
  • Anda punya waktu dan anggaran untuk mendedikasikan tes menulis
  • Bisnis membutuhkan bebas bug atau tingkat kesalahan yang rendah (keuangan, basis pengguna yang besar)

Terima kasih telah membaca artikel; Anda sekarang harus memiliki metode untuk pengujian yang dapat Anda gunakan secara default, yang dapat Anda andalkan saat Anda terdesak waktu.

Terkait:
  • Performa dan Efisiensi: Bekerja dengan HTTP/3
  • Tetap Terenkripsi, Tetap Aman: Bekerja dengan ESNI, DoH, dan DoT