Bagaimana cara saya menulis tes terhadap layanan yang pada akhirnya konsisten?

17

Saya sedang membangun layanan di atas Google App Engine Datastore, yang merupakan penyimpanan data yang pada akhirnya konsisten. Untuk aplikasi saya, ini baik-baik saja.

Namun, saya sedang mengembangkan tes yang melakukan hal-hal seperti objek PUT dan kemudian DAPATKAN objek dan memeriksa properti pada objek yang dikembalikan. Sayangnya, karena datastore pada akhirnya konsisten, tes sederhana ini tidak dapat diulang.

Bagaimana Anda menguji layanan yang pada akhirnya konsisten?

Doug Richardson
sumber
2
Mengapa Anda menguji reproduksibilitas yang diharapkan terhadap layanan eksternal?
... dan apa yang sebenarnya Anda coba untuk menguji? kode Anda? atau Google?
5
Saya sedang menguji seluruh sistem. Yaitu, itu adalah tes integrasi, bukan tes unit.
Doug Richardson
3
How can I reproducibly test an eventually consistent service? - Kamu tidak bisa. Anda harus menghapus kata "dapat direproduksi" atau kata "akhirnya;" Anda tidak dapat memiliki keduanya.
Robert Harvey
1
Jika pada akhirnya konsisten, apakah dapat direproduksi atau tidak, hasil apa pun akan menjadi sukses. Anda sudah mengatakan bahwa itu baik untuk aplikasi Anda, jadi apa yang sebenarnya Anda uji? Kemungkinan itu? Integrasi dengan GAE? Kode Anda?
Laiv

Jawaban:

16

Pertimbangkan persyaratan non-fungsional saat merancang tes fungsional Anda - jika layanan Anda memiliki persyaratan non-fungsional "Konsisten dalam waktu x (detik / menit / dll)", jalankan saja permintaan PUT, tunggu x, kemudian jalankan permintaan GET.

Pada saat itu, jika data belum 'tiba', Anda dapat menganggap permintaan PUT tidak sesuai dengan kebutuhan Anda.

Dan Ambrogio
sumber
7

Anda benar-benar ingin tes Anda cepat dan konsisten. Jika Anda mulai membuat tes yang kadang-kadang gagal karena konsistensi akhirnya, Anda akan mengabaikan tes ketika gagal, dan lalu apa gunanya?

Buat layanan palsu yang menangani permintaan PUT dan GET, tetapi memiliki operasi tambahan agar konsisten. Tes Anda kemudian:

datastore.do_put(myobj);
datastore.make_consistent();
validate(datastore.do_get(), myobj);

Ini memungkinkan Anda untuk menguji perilaku perangkat lunak Anda ketika GET berhasil mengambil objek PUT. Hal ini juga memungkinkan Anda untuk menguji perilaku perangkat lunak Anda ketika GET tidak menemukan objek (atau objek yang benar) karena layanan belum konsisten. Tinggalkan saja panggilan untuk make_consistent().

Masih layak memiliki tes yang berinteraksi dengan layanan nyata, tetapi harus berjalan di luar alur kerja pengembangan normal Anda, karena mereka tidak akan pernah dapat diandalkan 100% (misalnya jika layanan sedang down). Tes-tes ini harus digunakan untuk:

  1. memberikan metrik pada waktu kasus rata-rata dan terburuk antara PUT dan GET berikutnya menjadi konsisten; dan
  2. verifikasi bahwa layanan palsu Anda berperilaku serupa dengan layanan nyata. Lihat https://codewithoutrules.com/2016/07/31/verified-fakes/
Jonathan Giddy
sumber
6

OKE begitu. "What Are You Testing" adalah pertanyaan kuncinya.

  • Saya sedang menguji logika internal saya tentang apa yang terjadi dengan asumsi barang google berfungsi

Dalam hal ini Anda harus mengejek layanan google dan selalu mengembalikan respons.

  • Saya menguji logika saya dapat mengatasi kesalahan sementara yang saya tahu akan menghasilkan google

Dalam hal ini Anda harus mengejek layanan google dan selalu mengembalikan kesalahan sementara sebelum tanggapan yang benar

  • Saya menguji bahwa produk saya akan benar-benar berfungsi dengan layanan google asli

Anda harus menyuntikkan layanan google asli dan menjalankan tes. Tapi! Kode yang Anda uji harus memiliki penanganan Kesalahan Sementara (coba lagi) di dalamnya. JADI, Anda harus mendapatkan respons yang konsisten. (kecuali google berperilaku sangat buruk)

Ewan
sumber
+1 untuk saran Mock - Saya akan memberikan lebih banyak suara untuk opsi tambahan jika saya bisa.
mcottle
6

Gunakan salah satu dari yang berikut:

  • Setelah PUT, coba lagi GET N kali hingga sukses. Gagal jika tidak berhasil setelah N mencoba.
  • Tidur antara PUT dan GET

Sayangnya, Anda harus memilih nilai ajaib (N atau durasi tidur) untuk kedua teknik ini.

Doug Richardson
sumber
1
Bisakah Anda mengklarifikasi: apakah alternatif ini, atau saling melengkapi? Saya pikir Anda bermaksud mengatakan bahwa itu adalah alternatif - dan itulah yang saya pikirkan tentang mereka. Tapi mungkin saya salah.
Robin Green
1
Benar, saya berarti mereka menjadi alternatif.
Doug Richardson
2

Seperti yang saya pahami, Google Cloud datastore memungkinkan kueri yang sangat konsisten dan akhirnya konsisten .

Imbalannya adalah bahwa kueri yang sangat konsisten sangat terbatas pada tingkat suku bunga (sesuatu yang dapat Anda jalani selama pengujian).

Salah satu kemungkinan mungkin untuk menempatkan pertanyaan Anda ke datastore dalam pembungkus yang dapat memungkinkan konsistensi yang kuat untuk tujuan pengujian.

Misalnya, Anda dapat meminta metode dipanggil start_debug_strong_consistency()dan end_debug_strong_consistency().

Metode mulai akan membuat kunci yang dapat digunakan sebagai kunci leluhur untuk semua permintaan berikutnya, dan metode akhir akan menghapus kunci.

Satu-satunya perubahan pada pertanyaan aktual yang Anda uji adalah menelepon setAncestor(your_debug_key)jika kunci itu ada.


sumber
1

Salah satu pendekatan, yang bagus dalam teori tetapi mungkin tidak selalu praktis, adalah membuat semua operasi penulisan dalam sistem diuji idempoten . Itu berarti, dengan asumsi kode tes Anda menguji berbagai hal dalam urutan berurutan tetap, Anda dapat mencoba kembali semua membaca dan semua menulis secara individual sampai Anda mendapatkan hasil yang Anda harapkan, coba lagi hingga batas waktu yang Anda tentukan dalam kode uji terlampaui. Yaitu, lakukan hal A1, coba lagi jika perlu sampai hasilnya B1, kemudian lakukan hal A2, coba lagi jika perlu sampai hasilnya B2, dan seterusnya.

Maka Anda tidak perlu repot untuk memeriksa prasyarat operasi tulis, karena operasi tulis akan sudah memeriksa mereka untuk Anda, dan Anda hanya coba lagi sampai mereka berhasil!

Gunakan batas waktu "default" yang sama sebanyak mungkin, yang dapat ditingkatkan jika keseluruhan sistem menjadi lebih lambat, dan timpa default secara individual ketika mencoba kembali operasi yang sangat lambat.

Robin Green
sumber
1

Layanan seperti Google App Engine Datastore didasarkan pada replikasi data di beberapa titik keberadaan tersebar global (POP). Tes integrasi apa pun untuk layanan yang pada akhirnya konsisten benar-benar merupakan tes tingkat replikasi layanan tersebut di seluruh rangkaian POPs. Tingkat penyebaran konten ke setiap POP dalam layanan yang diberikan tidak akan sama dengan setiap POP dalam layanan tergantung pada sejumlah faktor, seperti metode replikasi dan berbagai masalah transportasi Internet - ini adalah dua contoh yang menyumbang sebagian besar laporan dalam layanan datastore yang pada akhirnya konsisten (setidaknya itu adalah pengalaman saya ketika saya bekerja untuk CDN utama).

Untuk menguji replikasi objek secara efektif di platform tertentu, Anda harus mengatur tes untuk meminta objek yang baru ditempatkan yang sama dari masing-masing POP layanan. Saya menyarankan pengujian daftar POP satu-ke-lima kali atau sampai semua POP di daftar POP Anda melaporkan memiliki objek. Berikut adalah serangkaian interval untuk melakukan tes yang bebas Anda sesuaikan: 1, 5, 60 menit, 12 jam, 25 jam setelah menempatkannya di datastore. Kuncinya adalah mencatat hasil pada setiap interval untuk ditinjau dan dianalisis kemudian untuk merasakan kemampuan layanan yang diberikan untuk mereplikasi objek secara global. Seringkali layanan datastore hanya menarik salinan lokal ke POP setelah diminta secara lokal [perutean dilakukan melalui protokol BGP, itulah sebabnya pengujian Anda harus meminta objek dari setiap POP tertentu agar valid secara global untuk platform yang diberikan] . Dalam kasus Google's Datastore Anda akan mencari untuk mengatur tes Anda untuk permintaan objek tertentu dari "lebih dari 70 titik kehadiran di 33 negara"; Anda mungkin harus mendapatkan daftar url alamat POP khusus dari Dukungan Google [ref:https://cloud.google.com/about/locations/ ] atau jika Google menggunakan Fastly untuk replikasi, Fastly Support [ https://www.fastly.com/resources ].

Beberapa keuntungan dari metode ini: 1) Anda akan merasakan platform replikasi layanan yang diberikan, mengetahui kekuatan dan kelemahannya secara keseluruhan pada skala global [seperti saat pengujian integrasi]. 2) Untuk objek apa pun yang Anda uji, Anda akan memiliki alat yang tersedia untuk menghangatkan konten [buat permintaan pertama yang membuat salinan di POP lokal yang diberikan] - sehingga memberi Anda cara untuk memastikan konten tersebar secara global sebelum klien Anda meminta dari dimanapun di bumi.

Rami Kuttaineh
sumber
0

Saya memiliki pengalaman dengan Google App Engine Datastore. Berjalan secara lokal, secara mengejutkan, seringkali lebih "akhirnya" daripada "konsisten". Contoh paling sederhana: buat entitas baru, lalu ambil kembali. Sering kali selama 5 tahun terakhir, saya telah melihat SDK yang berjalan secara lokal tidak menemukan entitas baru dengan segera, tetapi menemukannya setelah sekitar setengah detik.

Namun, berjalan melawan server Google yang sebenarnya, saya belum melihat perilaku itu. Mereka mencoba membuat klien Datastore Anda selalu berjalan melawan server yang sama di sisi mereka, jadi biasanya setiap perubahan segera tercermin dalam kueri.

Saran saya untuk pengujian integrasi adalah menjalankannya terhadap server nyata, dan kemudian Anda mungkin tidak perlu melakukan polling palsu atau penundaan untuk mendapatkan hasil Anda.

christian.simms
sumber
Meskipun nyaman, ini dapat menyebabkan kerusakan halus yang melibatkan beberapa server aplikasi tidak terdeteksi dalam tes integrasi Anda. Saya kira mereka membuat server lokal akhirnya konsisten untuk alasan yang bagus!
Robin Green