Bagaimana saya bisa menguji unit kelas yang membutuhkan panggilan layanan web?


21

Saya mencoba untuk menguji kelas yang memanggil beberapa layanan web Hadoop. Bentuknya hampir seperti:

method() {
    ...use Jersey client to create WebResource...
    ...make request...
    ...do something with response...
}

mis. ada metode buat direktori, metode buat folder dll.

Mengingat bahwa kode ini berurusan dengan layanan web eksternal yang tidak saya kendalikan, bagaimana saya bisa menguji unit ini? Saya bisa mencoba dan mengolok-olok klien layanan web / tanggapan tetapi itu melanggar pedoman yang saya lihat baru-baru ini: "Jangan mengejek objek yang tidak Anda miliki". Saya bisa mengatur implementasi layanan web dummy - apakah itu masih merupakan "unit test" atau apakah itu akan menjadi tes integrasi? Apakah tidak mungkin untuk menguji unit pada tingkat rendah ini - bagaimana praktisi TDD akan melakukan hal ini?


5
Di mana Anda melihat panduan tentang tidak mengejek hal-hal yang bukan milik Anda? Yang tampaknya menjadi alasan besar mengapa Anda harus mengejek hal ...
Thomas Owens


1
@ChrisCooper: Bolehkah saya menunjukkan bahwa tautan terakhir sudah sangat usang (dari 2007). Banyak yang telah berubah sejak saat itu. Saya mendapatkan perasaan dari pos bahwa mengejek jauh lebih sulit saat itu ketika Anda harus benar-benar menerapkan perilaku daripada hanya menggunakan kerangka kerja mengejek atau metaprogramming untuk mengatur nilai kembali ...
c_maker

Jawaban:


41

Menurut pendapat saya, Anda harus mengejek panggilan layanan web jika ini adalah tes unit, bukan tes integrasi.

Tes unit Anda tidak boleh menguji apakah layanan web eksternal berfungsi, atau apakah integrasi Anda dengan layanan itu benar. Tanpa terlalu dogmatis tentang TDD, perhatikan bahwa efek samping dari mengubah tes unit Anda menjadi tes integrasi adalah bahwa hal itu cenderung berjalan lebih lambat, dan Anda ingin tes unit cepat .

Juga, jika layanan web sementara tidak berfungsi atau salah, haruskah ini menyebabkan unit test Anda gagal? Sepertinya tidak benar. Uji unit Anda harus gagal hanya karena satu alasan: jika ada bug dalam kode di "unit" itu.

Satu-satunya bagian dari kode yang relevan di sini adalah ...do something with response.... Mengejek sisanya.


2
Ingatlah bahwa Anda harus menjaga tanda tangan objek tiruan Anda dan mengembalikan nilai dalam sinkronisasi dengan yang dihasilkan oleh layanan web Hadoop sepanjang perubahan yang tak terhindarkan pada layanan itu.
pcurry

Jarang ada yang layak menguji komponen yang tidak tersedia seperti Hadoop. Tetapi jika Anda memanggil layanan web khusus yang disediakan oleh tim atau organisasi lain, Anda mungkin ingin menulis tes untuk membela diri. Dengan begitu ketika ada masalah Anda dapat dengan cepat memeriksa apakah masalahnya adalah kode Anda atau layanan web. Ini bukan tes unit untuk dijalankan secara otomatis; ini merupakan diagnostik untuk dijalankan sesuai kebutuhan.
kevin cline

@ kevincline Saya sepenuhnya setuju tentang perlunya tes yang Anda usulkan, dan memang saya menuliskannya dalam pekerjaan harian saya dan telah membuktikan diri mereka berguna. Tapi mereka secara definisi BUKAN unit test, yang merupakan pertanyaan tentang :) Pertimbangkan ini: jika ini adalah unit test, dan kode gagal karena layanan web diubah, apa "unit" yang Anda uji? Apa yang sebenarnya gagal? Anda tidak menguji secara terpisah, seperti yang dibutuhkan oleh pengujian unit.
Andres F.

1
@AndresF .: Saya pikir kami dalam perjanjian kekerasan: "[diagnostik] ini BUKAN unit test ..."
kevin cline

@kevincline Benar! Saya salah membaca komentar Anda, maaf!
Andres F.

5

Saya tidak setuju dengan "jangan mengejek objek yang tidak Anda miliki" saat Anda sedang menguji unit.

Tujuan keberadaan mengejek adalah kenyataan bahwa akan ada modul, perpustakaan, kelas yang tidak akan kita miliki.

Saran saya untuk skenario Anda adalah mengejek panggilan layanan web.

Siapkan tiruan sedemikian rupa sehingga mengembalikan data ke modul Anda.
Pastikan Anda mencakup semua skenario, misalnya ketika data yang dikembalikan kembali adalah nol, ketika data yang dikembalikan kembali valid, dll.

Dan untuk kode yang Anda miliki, tanggung jawab Anda sebagai pengembang adalah memastikan bahwa kode yang Anda tulis berkinerja seperti yang diharapkan dalam semua skenario.


1

Saya akan menggunakan sesuatu seperti EasyMock untuk tes ini. Kerangka kerja mengejek adalah cara yang ideal untuk menghapus dependensi luar di kelas dan memberi Anda kontrol total atas hasil dependensi luar selama tes. Untuk sedikit memperluas contoh Anda:

class WebClass {

private WebServiceInterface webserviceInterface;

    void method(){
        R result = webServiceInterface.performWebServiceCall();
        ... do something with result
    }

    public void setWebServiceInterface(WebServiceInterface webServiceInterface){
        this.webServiceInterface = webServiceInterface;
    }
}


interface WebServiceInterface {

   R performWebServiceCall();

}


class WebClassTest {

private WebServiceInterface mock;    
private R sampleResult = new R();

    @Before
    public void before(){
        mock = EasyMock.createMock(WebServiceInterface.class);
    }


    @Test
    public void test() {
        WebClass classUnderTest = new WebClass();
        EasyMock.expect(mock.performWebServiceCall()).andReturn(sampleResult);
        classUnderTest.setWebServiceInterface(mock);
        classUnderTest.method();
        EasyMock.verify(mock);
    }
}

Hal pertama yang perlu Anda lakukan adalah mengekstrak logika di kelas Anda di mana Anda menggunakan Jersey untuk mendapatkan WebResource dan memanggil layanan web ke kelas yang terpisah. Membuat Antarmuka untuk kelas ini akan memungkinkan Anda untuk membuat tiruan yang kemudian dapat mendikte perilaku.

Setelah antarmuka ini dibuat, Anda dapat membuat tiruan menggunakan EasyMock, yang akan mengembalikan objek tertentu sesuai dengan kasus uji Anda. Contoh di atas adalah penyederhanaan bagaimana menyusun tes dasar yang diolok-olok dan bagaimana antarmuka Anda akan bekerja.

Untuk informasi lebih lanjut tentang kerangka kerja mengejek, silakan lihat pertanyaan ini . Juga, contoh ini mengasumsikan penggunaan Java tetapi kerangka kerja mengejek tersedia dalam semua bahasa dan meskipun mereka diterapkan secara berbeda, mereka akan bekerja secara umum dengan cara yang sama


1

Mengejek dapat diterima dalam kasus ini, tetapi Anda tidak membutuhkannya. Alih-alih pengujian unit method(), alih-alih pengujian unit hanya bagian yang menangani respons.

Ekstrak fungsi yang mengambil ResponseData(dari jenis apa pun yang sesuai) dan kemudian melakukan tindakan.

Alih-alih mengejek, sekarang Anda hanya membangun objek ResponseData dan meneruskannya.

Anda dapat meninggalkan panggilan layanan ke tes integrasi penuh - yang akan mencakup method()total


0

Apa yang telah saya lakukan, dan itu berhasil:

  1. Memiliki semua layanan panggilan kode melalui proxy.
  2. Proxy memanggil kelas yang secara statis tahu apakah kita menggunakan proxy atau tidak dan mengarahkan ulang sesuai itu. Mengejek hanyalah HashMaps yang untuk setiap permintaan mengembalikan balasan yang diberikan.
  3. Jalankan tes beberapa kali dalam urutan ini:

3.1. Pertama, semua layanan web diuji. Dari setiap mesin, bahkan mesin pengembang. Ini adalah layanan web nyata, tetapi berjalan di lingkungan pengembangan. Ini berarti layanan web tidak akan pernah bisa turun atau membalas nilai yang salah, karena jika tidak setiap pengembang mengeluh bahwa ia tidak dapat dikompilasi.

3.2 Kemudian semua tes unit internal ke aplikasi dijalankan. Ini berarti bahwa semua layanan web diejek dan diuji menjalankan tes yang sama dengan 3.1 (selain itu mereka harus lulus juga, jika tidak, tiruan itu salah), dan dipanggil oleh aplikasi nyata seolah-olah mereka benar-benar digunakan. Jika tiruannya salah, Anda dapat menjalankan tes di 3.1 dan mencatat nilai-nilai (permintaan, balas) di HashMap.

3.3 Kemudian tes yang sama seperti 3.2 dijalankan, tetapi kali ini terhadap layanan web nyata yang berjalan di lingkungan pengembangan.

Setelah semua ini selesai, untuk lingkungan produksi nyata Anda hanya perlu memberikan alamat asli untuk setiap layanan web. Semoga ini tidak memerlukan terlalu banyak perubahan dalam konfigurasi.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.