Bagaimana cara menguji kode yang tergantung pada API kompleks (misalnya Amazon S3)?

13

Saya berjuang dengan menguji metode yang mengunggah dokumen ke Amazon S3, tetapi saya pikir pertanyaan ini berlaku untuk segala API non-sepele / ketergantungan eksternal. Saya hanya datang dengan tiga solusi potensial tetapi tidak ada yang memuaskan:

  1. Jalankan kode, unggah dokumen, periksa dengan AWS's API bahwa itu telah diunggah dan hapus di akhir tes. Ini akan membuat tes sangat lambat, akan membutuhkan biaya setiap kali tes dijalankan dan tidak akan selalu mengembalikan hasil yang sama.

  2. Mock S3. Ini sangat berbulu karena saya tidak tahu tentang internal objek itu dan rasanya salah karena terlalu rumit.

  3. Pastikan saja MyObject.upload () dipanggil dengan argumen yang benar dan percaya bahwa saya menggunakan objek S3 dengan benar. Ini mengganggu saya karena tidak ada cara untuk mengetahui dengan pasti saya menggunakan API S3 dengan benar dari tes saja.

Saya memeriksa bagaimana Amazon menguji SDK mereka sendiri dan mereka mengejek segalanya. Mereka memiliki 200 helper helper yang melakukan ejekan. Saya merasa tidak praktis bagi saya untuk melakukan hal yang sama.

Bagaimana saya mengatasi ini?

springloaded
sumber
Bukan jawaban, tetapi dalam praktiknya kami menggunakan tiga pendekatan yang Anda ungkapkan.
jlhonora

Jawaban:

28

Ada dua masalah yang harus kita perhatikan di sini.

Yang pertama adalah bahwa Anda tampaknya melihat semua tes Anda dari perspektif tes unit. Tes unit sangat berharga, tetapi bukan satu-satunya jenis tes. Tes sebenarnya dapat dibagi menjadi beberapa lapisan yang berbeda, dari tes unit yang sangat cepat hingga tes integrasi yang kurang cepat hingga tes penerimaan yang lebih lambat . (Mungkin ada lebih banyak lapisan pecah, seperti tes fungsional .)

Yang kedua adalah bahwa Anda menggabungkan panggilan untuk kode pihak ketiga dengan logika bisnis Anda, menciptakan tantangan pengujian dan mungkin membuat kode Anda lebih rapuh.

Tes unit harus cepat dan harus sering dijalankan. Mengejek dependensi membantu agar tes ini berjalan cepat, tetapi berpotensi dapat menyebabkan lubang dalam cakupan jika ketergantungan berubah dan tiruan tidak. Kode Anda dapat rusak sementara tes Anda masih berjalan hijau. Beberapa perpustakaan mengejek akan memperingatkan Anda jika antarmuka ketergantungan berubah, yang lain tidak bisa.

Tes integrasi, di sisi lain, dirancang untuk menguji interaksi antara komponen, termasuk perpustakaan pihak ketiga. Mock tidak boleh digunakan pada tingkat pengujian ini karena kami ingin melihat bagaimana objek aktual berinteraksi bersama. Karena kami menggunakan objek nyata, tes ini akan lebih lambat, dan kami tidak akan menjalankannya hampir sesering unit kami menguji.

Tes penerimaan terlihat pada tingkat yang lebih tinggi, menguji apakah persyaratan untuk perangkat lunak terpenuhi. Tes-tes ini dijalankan terhadap keseluruhan, sistem lengkap yang akan dikerahkan. Sekali lagi, tidak ada ejekan yang harus digunakan.

Salah satu pedoman yang menurut orang berharga tentang mengolok-olok adalah dengan tidak mengolok-olok tipe yang tidak Anda miliki . Amazon memiliki API ke S3 sehingga mereka dapat memastikan itu tidak berubah di bawah mereka. Anda, di sisi lain, tidak memiliki jaminan ini. Karena itu, jika Anda mengejek API S3 dalam pengujian Anda, itu dapat mengubah dan memecahkan kode Anda, sementara semua pengujian Anda menunjukkan hijau. Jadi, bagaimana kita menyatukan kode uji yang menggunakan pustaka pihak ketiga?

Yah, kita tidak tahu. Jika kita mengikuti panduan ini, kita tidak bisa mengejek benda yang bukan milik kita. Tetapi ... jika kita memiliki ketergantungan langsung kita, kita dapat mengejeknya. Tapi bagaimana caranya? Kami membuat bungkus kami sendiri untuk API S3. Kita dapat membuatnya sangat mirip dengan API S3, atau kita dapat membuatnya sesuai dengan kebutuhan kita lebih dekat (lebih disukai). Kita bahkan dapat membuatnya sedikit lebih abstrak, katakan saja PersistenceServicedaripada AmazonS3Bucket. PersistenceServiceakan menjadi antarmuka dengan metode seperti #save(Thing)dan #fetch(ThingId), jenis metode yang mungkin ingin kita lihat (ini adalah contoh, Anda mungkin sebenarnya ingin metode yang berbeda). Kami sekarang dapat menerapkan PersistenceServicesekitar S3 API (katakanlah S3PersistenceService), merangkumnya dari kode panggilan kami.

Sekarang ke kode yang memanggil API S3. Kita perlu mengganti panggilan itu dengan panggilan ke PersistenceServiceobjek. Kami menggunakan injeksi ketergantungan untuk meneruskan kami PersistenceServiceke objek. Penting untuk tidak meminta S3PersistenceService, tetapi untuk meminta PersistenceService. Ini memungkinkan kami untuk menukar implementasi selama pengujian kami.

Semua kode yang digunakan untuk menggunakan API S3 secara langsung sekarang menggunakan kode kami PersistenceService, dan kode kami S3PersistenceServicesekarang membuat semua panggilan ke API S3. Dalam pengujian kami, kami dapat mengejek PersistenceService, karena kami memilikinya, dan menggunakan tiruan itu untuk memastikan bahwa kode kami membuat panggilan yang benar. Tapi sekarang tinggal bagaimana cara menguji S3PersistenceService. Ini memiliki masalah yang sama seperti sebelumnya: kami tidak dapat mengujinya tanpa memanggil ke layanan eksternal. Jadi ... kita tidak mengujinya. Kita bisa mengejek dependensi S3 API, tetapi ini akan memberi kita sedikit tambahan atau tidak ada kepercayaan tambahan. Sebagai gantinya, kami harus mengujinya di tingkat yang lebih tinggi: tes integrasi.

Ini mungkin terdengar agak mengganggu mengatakan bahwa kita seharusnya tidak menguji bagian dari kode kita, tetapi mari kita lihat apa yang telah kita capai. Kami memiliki banyak kode di semua tempat kami tidak dapat menguji unit yang sekarang dapat diuji melalui unit PersistenceService. Kami memiliki kekacauan perpustakaan pihak ketiga kami terbatas pada kelas implementasi tunggal. Kelas itu harus menyediakan fungsionalitas yang diperlukan untuk menggunakan API, tetapi tidak memiliki logika bisnis eksternal yang menyertainya. Oleh karena itu, setelah ditulis, itu harus sangat stabil dan tidak banyak berubah. Kita dapat mengandalkan tes yang lebih lambat yang tidak sering kita jalankan karena kodenya stabil.

Langkah selanjutnya adalah menulis tes integrasi untuk S3PersistenceService. Ini harus dipisahkan dengan nama atau folder sehingga kita dapat menjalankannya secara terpisah dari pengujian unit cepat kami. Tes integrasi sering dapat menggunakan kerangka kerja pengujian yang sama dengan pengujian unit jika kodenya cukup informatif, jadi kita tidak perlu mempelajari alat baru. Kode aktual untuk tes integrasi adalah apa yang akan Anda tulis untuk Opsi 1 Anda.

cbojar
sumber
pertanyaannya adalah bagaimana Anda menjalankan tes integrasi atau lebih tepatnya e2e untuk API yang Anda paparkan. Anda tidak dapat mengejek PersistenceService karena alasan yang jelas. Entah saya salah memahami sesuatu, atau menambahkan lapisan lain di antara API aplikasi dan AWS API, memberi Anda tidak lebih dari memiliki waktu lebih mudah melakukan pengujian unit
Yerken
@ Yerken Ketika saya memikirkannya, saya cukup yakin saya bisa mengisi jawaban panjang lain untuk pertanyaan itu. Itu bahkan mungkin bermanfaat bagi Anda karena Anda mungkin mendapatkan lebih dari sekadar tanggapan saya.
cbojar
4

Anda harus melakukan keduanya.

Menjalankan, mengunggah, dan menghapus adalah ujian integrasi. Ini berinteraksi dengan sistem eksternal dan karenanya dapat berjalan lambat. Mungkin seharusnya tidak menjadi bagian dari setiap bangunan yang Anda lakukan secara lokal, tetapi harus menjadi bagian dari pembangunan CI atau bangunan malam. Itu mengimbangi kelambatan dari tes-tes tersebut dan masih memberikan nilai untuk diuji secara otomatis.

Anda juga perlu unittests yang berjalan lebih cepat. Karena umumnya pintar untuk tidak terlalu bergantung pada sistem eksternal (sehingga Anda dapat menukar implementasi atau beralih), Anda mungkin harus mencoba dan menulis antarmuka sederhana di atas S3 yang dapat Anda kode. Mock antarmuka itu di unittests sehingga Anda dapat memiliki unittests yang berjalan cepat.

Tes pertama memeriksa apakah kode Anda benar-benar berfungsi dengan S3, tes kedua kode Anda benar memanggil kode yang berbicara ke S3.

JDT
sumber
2

Saya akan mengatakan bahwa itu tergantung pada kompleksitas penggunaan API Anda .

  1. Anda pasti perlu melakukan setidaknya beberapa pengujian yang benar-benar memanggil API S3 dan mengonfirmasi bahwa itu bekerja dari ujung ke ujung.

  2. Anda juga pasti perlu melakukan pengujian tambahan yang tidak benar-benar memanggil API, sehingga Anda dapat menguji perangkat lunak Anda sendiri secara memadai tanpa harus selalu menggunakan API.

Pertanyaan yang tersisa adalah: apakah Anda perlu mengejek API?

Dan saya pikir itu tergantung pada seberapa banyak Anda melakukannya. Jika Anda hanya melakukan satu atau dua tindakan sederhana, saya rasa Anda tidak perlu repot-repot melakukan mock-up. Saya akan puas hanya dengan memeriksa penggunaan fungsi dan melakukan beberapa pengujian langsung.

Namun, jika penggunaannya lebih kompleks, dengan skenario berbeda dan variabel berbeda yang dapat memengaruhi hasil, Anda mungkin perlu mengejeknya untuk melakukan pengujian yang lebih menyeluruh.


sumber
1

Menambah jawaban sebelumnya, pertanyaan utamanya adalah apakah (dan bagaimana) Anda ingin mengejek S3 API untuk pengujian Anda.

Alih-alih mengolok-olok tanggapan S3 individual, Anda dapat memanfaatkan beberapa kerangka kerja mengejek yang ada yang sangat canggih. Misalnya moto menyediakan fungsionalitas yang sangat mirip dengan API S3 yang sebenarnya.

Anda juga bisa melihat LocalStack , kerangka kerja yang menggabungkan alat yang ada dan menyediakan lingkungan cloud lokal yang berfungsi penuh (termasuk S3) yang memfasilitasi pengujian integrasi.

Meskipun beberapa alat ini ditulis dalam bahasa lain (Python), seharusnya mudah untuk memutar lingkungan pengujian dalam proses eksternal dari pengujian Anda di, katakanlah, Java / JUnit.

kekesalan
sumber