Apa perbedaan antara mock & stub?

963

Saya telah membaca berbagai artikel tentang mengejek dan tidak melakukan pengujian, termasuk Martin Fowler's Mocks Aron't Stubs , tetapi masih tidak mengerti perbedaannya.

never_had_a_name
sumber
75
@OP Karena tidak ada perbedaan. Artikel ini, sebanyak dicintai oleh komunitas, adalah - dengan segala hormat - membuat semuanya tidak perlu membingungkan dengan menambahkan makna tambahan pada kata-kata yang mudah dipahami sebaliknya dan dengan membuat hal-hal yang tidak perlu rumit. Mock hanyalah tiruan, sesuatu yang menjalankan logika bisnis palsu dan bukan logika asli. Memeriksa perilaku pada akhirnya adalah pilihan Anda, tetapi itu masih pura-pura. Atau apa pun yang Anda ingin menyebutnya, tetapi buat SATU. Jangan membelah rambut. Sederhanakan, sehingga orang dapat memahami konsep Anda dengan mudah - yang gagal dengan artikel di atas.
WST
10
"Klasifikasi antara ejekan, palsu, dan bertopik sangat tidak konsisten di seluruh literatur." Dengan banyak kutipan. Masih salah satu kutipan Wikipedia favorit saya - jika ada hal seperti itu :) en.wikipedia.org/wiki/Mock_object
JD.
11
bahwa artikel Martin Fowler benar-benar sulit dimengerti untuk pemula.
lmiguelvargasf
1
Cara saya memahaminya adalah bahwa rintisan hanya akan menjadi benda yang dibuang untuk tes Anda, seperti kumpulan data dummy. Mock akan menjadi versi pintar yang ditimpa secara cerdik dari sesuatu yang lebih kompleks, seperti lapisan layanan dengan berbagai metode, yang mungkin telah Anda ubah perilaku, untuk pengujian Anda. Dua hal tersebut digunakan bersama-sama, seperti Anda bisa memasukkan beberapa benda yang sudah di-stub ke dalam lapisan tiruan Anda.
JsonStatham

Jawaban:

746

Rintisan

Saya percaya perbedaan terbesar adalah bahwa rintisan yang telah Anda tulis dengan perilaku yang telah ditentukan. Jadi Anda akan memiliki kelas yang mengimplementasikan dependensi (kelas abstrak atau antarmuka yang paling mungkin) Anda berpura-pura untuk tujuan pengujian dan metode hanya akan terhapus dengan tanggapan yang ditetapkan. Mereka tidak akan melakukan sesuatu yang mewah dan Anda akan sudah menulis kode stubbed untuk itu di luar ujian Anda.

Mengejek

Mock adalah sesuatu yang sebagai bagian dari pengujian Anda, Anda harus mengatur dengan harapan Anda. Mock tidak diatur dengan cara yang telah ditentukan sehingga Anda memiliki kode yang melakukannya dalam pengujian Anda. Mengolok-olok dengan cara ditentukan pada saat runtime karena kode yang menetapkan harapan harus dijalankan sebelum mereka melakukan apa pun.

Perbedaan antara Mocks dan Stubs

Tes yang ditulis dengan mengejek biasanya mengikuti initialize -> set expectations -> exercise -> verifypola pengujian. Sementara rintisan pra-tertulis akan mengikuti initialize -> exercise -> verify.

Kesamaan antara Mocks dan Rintisan

Tujuan keduanya adalah untuk menghilangkan pengujian semua dependensi kelas atau fungsi sehingga tes Anda lebih fokus dan lebih sederhana dalam apa yang mereka coba buktikan.

Sean Copenhaver
sumber
876

Kata pengantar

Ada beberapa definisi objek, yang tidak nyata. Istilah umum adalah tes ganda . Istilah ini meliputi: dummy , fake , stub , mock .

Referensi

Menurut artikel Martin Fowler :

  • Benda-benda boneka diedarkan tetapi tidak pernah benar-benar digunakan. Biasanya mereka hanya digunakan untuk mengisi daftar parameter.
  • Objek palsu sebenarnya memiliki implementasi yang berfungsi, tetapi biasanya mengambil beberapa jalan pintas yang membuatnya tidak cocok untuk produksi (database memori adalah contoh yang baik).
  • Rintisan bertopik memberikan jawaban kalengan untuk panggilan yang dilakukan selama tes, biasanya tidak menanggapi apa pun di luar apa yang diprogram dalam tes. Rintisan bertopik juga dapat merekam informasi tentang panggilan, seperti rintisan gateway email yang mengingat pesan yang 'dikirim', atau mungkin hanya berapa banyak pesan yang 'dikirim'.
  • Mengolok - olok adalah apa yang kita bicarakan di sini: objek diprogram dengan harapan yang membentuk spesifikasi panggilan mereka diharapkan untuk menerima.

Gaya

Mengejek vs bertopik = Pengujian perilaku vs pengujian Negara

Prinsip

Menurut prinsip Uji hanya satu hal per tes , mungkin ada beberapa bertopik dalam satu tes, tetapi umumnya hanya ada satu ejekan.

Lingkaran kehidupan

Tes siklus hidup dengan bertopik:

  1. Pengaturan - Mempersiapkan objek yang sedang diuji dan kolaborator bertopiknya.
  2. Latihan - Uji fungsionalitasnya.
  3. Status verifikasi - Gunakan konfirmasi untuk memeriksa status objek.
  4. Teardown - Bersihkan sumber daya.

Tes siklus hidup dengan mengolok-olok:

  1. Setup data - Mempersiapkan objek yang sedang diuji.
  2. Setup harapan - Siapkan harapan dalam tiruan yang sedang digunakan oleh objek utama.
  3. Latihan - Uji fungsionalitasnya.
  4. Verifikasi harapan - Verifikasikan bahwa metode yang benar telah dipanggil dalam tiruan.
  5. Status verifikasi - Gunakan konfirmasi untuk memeriksa status objek.
  6. Teardown - Bersihkan sumber daya.

Ringkasan

Kedua pengujian mengejek dan bertopik memberikan jawaban untuk pertanyaan: Apa hasilnya?

Pengujian dengan mengolok-olok juga tertarik pada: Bagaimana hasilnya telah dicapai?

Ryszard Dżegan
sumber
Tunggu, ejekan juga mengembalikan jawaban kalengan? Sebab, mengapa mereka menjawab pertanyaan itu?
AturSams
Dari apa yang Anda tulis, saya dapat mengatakan bahwa ejekan = bertopik + harapan dan verifikasi, karena ejekan "memberikan jawaban kalengan untuk panggilan yang dibuat selama tes, biasanya tidak menanggapi sama sekali untuk apa pun di luar apa yang diprogram dalam untuk tes" (sama seperti bertopik). Dan contoh yang ditunjukkan Fowler sebagai contoh rintisan sebenarnya adalah contoh mata-mata! Itu berarti bahwa tiruan adalah rintisan, dan mata-mata adalah rintisan. Dan sebuah rintisan hanyalah sebuah objek yang memiliki beberapa metode kerja. Itu juga menjelaskan mengapa metode Mockito usang stub ().
kolobok
Apa yang saya anggap membingungkan tentang ini dan jawaban yang diterima adalah "pengaturan harapan" ini, apa artinya? Biasanya, dalam "kode utama" Anda membuat hasil yang Anda harapkan. Kedengarannya seperti Anda meletakkan harapan entah bagaimana ke objek tiruan, yang tidak masuk akal bagi saya. JUGA, Anda bisa dengan mudah melatih tiruan dengan beberapa input, menyimpan hasilnya, membuat "harapan" nanti dan kemudian membandingkan. Anda menggunakan terminologi yang menurut saya terlalu abstrak dan ambigu.
IceFire
365

Rintisan adalah objek palsu sederhana. Itu hanya memastikan tes berjalan lancar.
Mock adalah rintisan yang lebih cerdas. Anda memverifikasi tes Anda melewati itu.

Arnis Lapsa
sumber
33
Saya pikir ini adalah jawaban yang paling ringkas dan tepat. Takeaway: sebuah rintisan IS-A tiruan. stackoverflow.com/a/17810004/2288628 adalah versi yang lebih panjang dari jawaban ini.
PoweredByRice
8
Saya tidak berpikir mock adalah sebuah rintisan. Mengejek digunakan untuk menegaskan dan tidak boleh mengembalikan data, bertopik digunakan untuk mengembalikan data dan tidak boleh menegaskan.
dave1010
2
@ dave1010 Mocks paling pasti bisa mengembalikan data atau bahkan melempar pengecualian. Mereka harus melakukannya sebagai tanggapan terhadap param yang diteruskan ke mereka.
Trenton
2
@trenton jika suatu objek kembali atau melempar berdasarkan data yang dimasukkan maka itu palsu , bukan tiruan. Rintisan bertopik bagaimana SUT Anda menangani menerima pesan, mengejek menguji bagaimana SUT Anda mengirim pesan. Mencampur 2 kemungkinan akan menyebabkan desain OO yang buruk.
dave1010
8
Saya pikir ini hebat - sebuah rintisan mengembalikan jawaban atas pertanyaan. Mock juga mengembalikan jawaban atas pertanyaan (is-a stub) tetapi juga memverifikasi bahwa pertanyaan itu ditanyakan !!
Leif
238

Berikut adalah deskripsi masing-masing yang diikuti dengan sampel dunia nyata.

  • Dummy - hanya nilai palsu untuk memuaskan API.

    Contoh : Jika Anda menguji metode kelas yang membutuhkan banyak parameter wajib dalam konstruktor yang tidak berpengaruh pada pengujian Anda, maka Anda dapat membuat objek dummy untuk tujuan membuat instance kelas baru.

  • Palsu - buat implementasi tes kelas yang mungkin memiliki ketergantungan pada beberapa infrastruktur eksternal. (Ini adalah praktik yang baik bahwa unit test Anda TIDAK benar-benar berinteraksi dengan infrastruktur eksternal.)

    Contoh : Buat implementasi palsu untuk mengakses database, ganti dengan in-memorykoleksi.

  • Stub - menimpa metode untuk mengembalikan nilai-nilai hard-coded, juga disebut sebagai state-based.

    Contoh : Kelas tes Anda bergantung pada metode yang Calculate()membutuhkan waktu 5 menit untuk menyelesaikannya. Daripada menunggu 5 menit, Anda dapat mengganti implementasi nyata dengan rintisan yang mengembalikan nilai-nilai hard-coded; hanya mengambil sebagian kecil dari waktu.

  • Mengejek - sangat mirip dengan Stubtetapi interaction-basedbukan berbasis negara. Ini berarti Anda tidak mengharapkan dariMock untuk mengembalikan nilai, tetapi untuk berasumsi bahwa urutan tertentu dari pemanggilan metode dilakukan.

    Contoh: Anda sedang menguji kelas pendaftaran pengguna. Setelah meneleponSave , itu harus memanggil SendConfirmationEmail.

Stubsdan Mockssebenarnya adalah sub-tipe dari Mock, keduanya menukar implementasi nyata dengan implementasi tes, tetapi untuk alasan yang berbeda dan spesifik.

Im
sumber
175

Dalam kursus codeschool.com , Pengujian Rails untuk Zombies , mereka memberikan definisi persyaratan:

Rintisan

Untuk mengganti metode dengan kode yang mengembalikan hasil yang ditentukan.

Mengejek

Sebuah rintisan dengan pernyataan bahwa metode dipanggil.

Jadi seperti yang dijelaskan Sean Copenhaver dalam jawabannya, perbedaannya adalah bahwa mengejek menetapkan harapan (yaitu membuat pernyataan, tentang apakah atau bagaimana mereka dipanggil).

Dillon Kearns
sumber
Untuk melengkapi posting Dillon, pikirkan ini, Anda memiliki Kelas yang disebut "MakeACake" yang mengambil beberapa perpustakaan: Susu, Telur, Gula, Oven.
aarkerio
139

Rintisan bertopik jangan gagal tes Anda, mock bisa.

mk_
sumber
2
Dan saya pikir ini bagus, Anda tahu jika tes memiliki perilaku yang sama setelah refactoring.
RodriKing
1
@RodriKing, saya memiliki perasaan yang sama. Seperti Mock, dengan perubahan apa pun dalam kode produksi - Anda memiliki perubahan yang sesuai dengan kode pengujian. Yang itu sakit! Dengan Rintisan bertopik, Rasanya seperti Anda terus menguji perilaku sehingga tidak perlu perubahan mikro dengan kode uji.
tucq88
35

Saya pikir jawaban yang paling sederhana dan jelas tentang pertanyaan ini diberikan dari Roy Osherove dalam bukunya The art of Unit Testing (halaman 85)

Cara termudah untuk memberi tahu bahwa kita sedang berurusan dengan rintisan adalah dengan memperhatikan bahwa rintisan itu tidak akan pernah gagal dalam ujian. Menegaskan penggunaan tes selalu bertentangan dengan kelas yang diuji.

Di sisi lain, tes akan menggunakan objek tiruan untuk memverifikasi apakah tes gagal atau tidak. [...]

Sekali lagi, objek tiruan adalah objek yang kita gunakan untuk melihat apakah tes gagal atau tidak.

Itu berarti jika Anda membuat pernyataan palsu, itu berarti Anda menggunakan palsu sebagai tiruan, jika Anda menggunakan palsu hanya untuk menjalankan tes tanpa pernyataan di atasnya Anda menggunakan palsu sebagai rintisan.

Ghini Antonio
sumber
2
Saya berharap jawaban Anda akan menemukan jalannya ke atas. Inilah R. Osherove yang menjelaskan youtu.be/fAb_OnooCsQ?t=1006 ini .
Michael Ekoka
31

Membaca semua penjelasan di atas, izinkan saya mencoba untuk menyingkat:

  • Rintisan : sepotong kode yang memungkinkan tes berjalan, tetapi Anda tidak peduli apa yang terjadi padanya.
  • Mock : sepotong kode, yang Anda VERIFIKASI disebut dengan benar sebagai bagian dari tes.
  • Mata-mata : sepotong kode boneka, yang memotong beberapa panggilan ke bagian kode yang nyata, memungkinkan Anda memverifikasi panggilan tanpa mengganti seluruh objek asli.
O'Rooney
sumber
4
Jawaban yang bagus. Mock terdengar sangat mirip dengan Spy, berdasarkan definisi Anda. Akan lebih baik jika Anda memperbarui jawaban Anda untuk memasukkan beberapa tes ganda lagi.
Rowan Gontier
Saya belum pernah mendengar tentang Spy ketika saya menulis jawaban ini.
O'Rooney
23

Mock hanya menguji perilaku, memastikan metode tertentu dipanggil. Stub adalah versi yang dapat diuji (per se) dari objek tertentu.

Apa maksud Anda cara Apple?

NebulaFox
sumber
19
"Apa maksudmu cara Apple?" Gunakan Helvetica
kubi
7
Dalam cara Apple yang bertentangan dengan cara Microsoft :)
never_had_a_name
2
Apakah ini membantu situasi?
NebulaFox
21

Jika Anda membandingkannya dengan debugging:

Stub seperti memastikan metode mengembalikan nilai yang benar

Mock seperti benar-benar melangkah ke dalam metode dan memastikan semua yang ada di dalamnya benar sebelum mengembalikan nilai yang benar.

lebih baik lagi
sumber
20

Menggunakan model mental benar-benar membantu saya memahami ini, daripada semua penjelasan dan artikel, yang tidak cukup "meresap".

Bayangkan anak Anda memiliki piring kaca di atas meja dan dia mulai bermain dengannya. Sekarang, Anda khawatir itu akan pecah. Jadi, Anda memberinya piring plastik saja. Itu akan menjadi Mock (perilaku yang sama, antarmuka yang sama, implementasi "lebih lembut").

Sekarang, katakan Anda tidak memiliki pengganti plastik, jadi Anda menjelaskan "Jika Anda terus bermain dengan itu, itu akan rusak!". Itu sebuah rintisan , Anda memberikan status yang telah ditentukan sebelumnya.

A Dummy akan menjadi garpu yang bahkan tidak dia gunakan ... dan Spy bisa menjadi sesuatu seperti memberikan penjelasan yang sama yang sudah Anda gunakan yang berfungsi.

Moshisho
sumber
19

Saya pikir perbedaan paling penting di antara mereka adalah niat mereka.

Biarkan saya mencoba menjelaskannya di MENGAPA rintisan vs MENGAPA tiruan

Misalkan saya sedang menulis kode uji untuk pengontrol waktu publik mac twitter klien saya

Berikut adalah contoh kode uji

twitter_api.stub(:public_timeline).and_return(public_timeline_array)
client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)
controller.refresh_public_timeline
  • STUB: Koneksi jaringan ke twitter API sangat lambat, yang membuat pengujian saya lambat. Saya tahu itu akan mengembalikan garis waktu, jadi saya membuat rintisan mensimulasikan HTTP twitter API, sehingga pengujian saya akan berjalan sangat cepat, dan saya dapat menjalankan tes bahkan saya sedang offline.
  • MOCK: Saya belum menulis metode UI saya, dan saya tidak yakin metode apa yang perlu saya tulis untuk objek ui saya. Saya berharap tahu bagaimana controller saya akan berkolaborasi dengan objek ui saya dengan menulis kode tes.

Dengan menulis tiruan, Anda menemukan hubungan objek kolaborasi dengan memverifikasi harapan terpenuhi, sementara rintisan hanya mensimulasikan perilaku objek.

Saya sarankan untuk membaca artikel ini jika Anda ingin tahu lebih banyak tentang ejekan: http://jmock.org/oopsla2004.pdf

Joe Yang
sumber
1
Saya pikir Anda memiliki ide yang tepat, tetapi Dillon Kearns menjelaskannya lebih jelas.
O'Rooney
19

Agar sangat jelas dan praktis:

Stub: Kelas atau objek yang mengimplementasikan metode kelas / objek untuk dipalsukan dan selalu mengembalikan apa yang Anda inginkan.

Contoh dalam JavaScript:

var Stub = {
   method_a: function(param_a, param_b){
      return 'This is an static result';
   }
}

Mock: Sama dengan rintisan, tetapi ia menambahkan beberapa logika yang "memverifikasi" ketika suatu metode dipanggil sehingga Anda bisa yakin beberapa implementasi memanggil metode itu.

Seperti yang dikatakan @mLevan bayangkan sebagai contoh bahwa Anda menguji kelas pendaftaran pengguna. Setelah memanggil Simpan, itu harus memanggil SendConfirmationEmail.

Contoh kode yang sangat bodoh:

var Mock = {
   calls: {
      method_a: 0
   }

   method_a: function(param_a, param_b){
     this.method_a++; 
     console.log('Mock.method_a its been called!');
   }
}
R01010010
sumber
16

Slide ini menjelaskan perbedaan utama yang sangat baik.

masukkan deskripsi gambar di sini

* Dari CSE 403 Kuliah 16, University of Washington (slide dibuat oleh "Marty Stepp")

Aviram Fireberger
sumber
Ini adalah penjelasan yang lebih jelas tentang perbedaan antara keduanya, IMO. Untuk rintisan: penguji mengambil rintisan dan menggunakannya langsung di dalam kelas yang diuji. Tetapi untuk Mock, tester harus menggunakan cara bagaimana objek Mock akan digunakan. Dalam kasus yang berbeda, itu akan berperilaku berbeda. Sebaliknya, rintisan tidak diharapkan untuk berperilaku berbeda tetapi digunakan sebagaimana adanya (artinya mengembalikan data yang sama setiap kali dihubungi)
Dexter
12

Saya suka penjelasan yang dikeluarkan oleh Roy Osherove [tautan video] .

Setiap kelas atau objek yang dibuat adalah Palsu. Itu adalah Mock jika Anda memverifikasi panggilan yang menentangnya. Kalau tidak, itu sebuah rintisan.

nitishagar
sumber
12
  • Rintisan bertopik vs mengejek
    • Rintisan bertopik
      1. memberikan jawaban spesifik untuk pemanggilan metode
        • mis: myStubbedService.getValues ​​() baru saja mengembalikan sebuah String yang diperlukan oleh kode yang diuji
      2. digunakan oleh kode yang sedang diuji untuk mengisolasinya
      3. tidak bisa gagal tes
        • mis: myStubbedService.getValues ​​() baru saja mengembalikan nilai stubbed
      4. sering menerapkan metode abstrak
    • Mengejek
      1. "superset" dari bertopik; dapat menyatakan bahwa metode tertentu dipanggil
        • mis: verifikasi bahwa myMockedService.getValues ​​() dipanggil hanya sekali
      2. digunakan untuk menguji perilaku kode yang diuji
      3. bisa gagal tes
        • mis: verifikasi bahwa myMockedService.getValues ​​() dipanggil sekali; verifikasi gagal, karena myMockedService.getValues ​​() tidak dipanggil oleh kode yang saya uji
      4. sering mengejek antarmuka
Relu Mesaros
sumber
11

lihat Uji Ganda:

  • Palsu : Palsu adalah objek yang memiliki implementasi yang berfungsi, tetapi tidak sama dengan yang produksi.Seperti : implementasi dalam memori objek data atau repositori.
  • Rintisan : Rintisan adalah objek yang menyimpan data yang telah ditentukan dan menggunakannya untuk menjawab panggilan selama tes. Seperti : objek yang perlu mengambil beberapa data dari database untuk merespons pemanggilan metode.

  • Mengolok-olok : Mengejek adalah benda yang mendaftarkan panggilan yang mereka terima. Dalam pernyataan pengujian, kami dapat memverifikasi pada Mocks bahwa semua tindakan yang diharapkan dilakukan. Seperti : fungsi yang memanggil layanan pengiriman e-mail. untuk lebih lanjut periksa ini .

Alireza Rahmani Khalili
sumber
1
jawaban terbaik menurut saya
Ero Stefano
9

Sebuah palsu adalah istilah generik yang dapat digunakan untuk menggambarkan baik rintisan atau objek tiruan (tulisan tangan atau sebaliknya), karena mereka berdua terlihat seperti objek nyata.

Apakah sebuah rintisan palsu atau tiruan tergantung pada bagaimana itu digunakan dalam tes saat ini. Jika digunakan untuk memeriksa interaksi (dinyatakan melawan), itu adalah objek tiruan. Kalau tidak, itu sebuah rintisan.

Palsu membuat tes berjalan dengan lancar. Ini berarti bahwa pembaca tes masa depan Anda akan memahami apa yang akan menjadi perilaku objek palsu, tanpa perlu membaca kode sumbernya (tanpa harus bergantung pada sumber daya eksternal).

Apa artinya tes berjalan dengan lancar?
Contohnya dalam kode di bawah ini:

 public void Analyze(string filename)
        {
            if(filename.Length<8)
            {
                try
                {
                    errorService.LogError("long file entered named:" + filename);
                }
                catch (Exception e)
                {
                    mailService.SendEMail("[email protected]", "ErrorOnWebService", "someerror");
                }
            }
        }

Anda ingin menguji metode mailService.SendEMail () , untuk melakukan itu Anda perlu mensimulasikan Pengecualian dalam metode pengujian Anda, jadi Anda hanya perlu membuat kelas errorService Stub Palsu untuk mensimulasikan hasil itu, maka kode tes Anda akan dapat menguji metode mailService.SendEMail (). Seperti yang Anda lihat, Anda perlu mensimulasikan hasil yang berasal dari kelas ErrorService Ketergantungan Eksternal lainnya.

Mustafa Ekici
sumber
8

Langsung dari makalah Peran Mock, bukan Objects , oleh pengembang jMock:

Rintisan bertopik adalah implementasi dummy kode produksi yang mengembalikan hasil kalengan. Objek Mock bertindak sebagai bertopik, tetapi juga termasuk pernyataan untuk instrumen interaksi objek target dengan tetangganya.

Jadi, perbedaan utamanya adalah:

  • ekspektasi yang ditetapkan pada stub biasanya generik, sementara ekspektasi yang ditetapkan pada tiruan bisa lebih "pintar" (misalnya mengembalikan ini pada panggilan pertama, ini pada yang kedua dll).
  • stubs biasanya digunakan untuk mengatur input SUT tidak langsung , sementara tiruan dapat digunakan untuk menguji input tidak langsung dan output tidak langsung dari SUT.

Singkatnya, sementara juga mencoba untuk membubarkan kebingungan dari judul artikel Fowler : ejekan adalah bertopik, tetapi mereka tidak hanya bertopik .

Dimos
sumber
1
Saya pikir Anda benar, tetapi inilah mengapa artikel Fowler membingungkan, judul artikelnya adalah "Mocks Arn't Stubs" ... tetapi mereka BEGITU ?! ¯_ (ツ) _ / ¯
stonedauwg
@stonedauwg, memang, saya mengedit posting saya untuk memasukkan permainan kata dan klarifikasi Anda. Semoga ini bisa membantu sedikit lagi.
Dimos
@stonedauwg, mock bukanlah sebuah rintisan, seperti persegi panjang bukan persegi. :)
seanriordan08
7

Saya membaca The Art of Unit Testing , dan menemukan definisi berikut:

Sebuah palsu adalah istilah generik yang dapat digunakan untuk menggambarkan baik rintisan atau objek tiruan (tulisan tangan atau sebaliknya), karena mereka berdua terlihat seperti objek nyata. Apakah sebuah rintisan palsu atau tiruan tergantung pada bagaimana itu digunakan dalam tes saat ini. jika digunakan untuk memeriksa interaksi (dinyatakan melawan), itu adalah objek tiruan . Kalau tidak, itu sebuah rintisan .

Afonso Matos
sumber
5

Saya menemukan artikel menarik ini oleh UncleBob The Little Mocker . Ini menjelaskan semua terminologi dengan cara yang sangat mudah dimengerti, jadi ini berguna untuk pemula. Artikel Martin Fowlers sulit dibaca terutama untuk pemula seperti saya.

AI
sumber
4

Stub membantu kita menjalankan tes. Bagaimana? Ini memberikan nilai yang membantu menjalankan tes. Nilai-nilai ini sendiri tidak nyata dan kami menciptakan nilai-nilai ini hanya untuk menjalankan tes. Misalnya kita membuat HashMap untuk memberi kita nilai yang mirip dengan nilai dalam tabel database. Jadi alih-alih berinteraksi langsung dengan basis data, kami berinteraksi dengan Hashmap.

Mock adalah objek palsu yang menjalankan tes. tempat kami menegaskan.

Harry
sumber
"Jadi, alih-alih berinteraksi langsung dengan basis data, kami berinteraksi dengan Hashmap." ... karena kita belum punya waktu untuk membuat kode Modul-Database, dan kita tidak bisa menjalankan kode-tes tanpa menggunakan rintisan. Kalau tidak, Hasmap yang sama akan menjadi tiruan! Baik?
Boris Däppen
4

Lihat di bawah ini contoh tiruan vs bertopik menggunakan C # dan Moq framework. Moq tidak memiliki kata kunci khusus untuk Rintisan tetapi Anda dapat menggunakan objek Mock untuk membuat rintisan juga.

namespace UnitTestProject2
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    [TestClass]
    public class UnitTest1
    {
        /// <summary>
        /// Test using Mock to Verify that GetNameWithPrefix method calls Repository GetName method "once" when Id is greater than Zero
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
        {
            // Arrange 
            var mockEntityRepository = new Mock<IEntityRepository>();
            mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));

            var entity = new EntityClass(mockEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(12);
            // Assert
            mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Once);
        }
        /// <summary>
        /// Test using Mock to Verify that GetNameWithPrefix method doesn't call Repository GetName method when Id is Zero
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
        {
            // Arrange 
            var mockEntityRepository = new Mock<IEntityRepository>();
            mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
            var entity = new EntityClass(mockEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(0);
            // Assert
            mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Never);
        }
        /// <summary>
        /// Test using Stub to Verify that GetNameWithPrefix method returns Name with a Prefix
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
        {
            // Arrange 
            var stubEntityRepository = new Mock<IEntityRepository>();
            stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
                .Returns("Stub");
            const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
            var entity = new EntityClass(stubEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(12);
            // Assert
            Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
        }
    }
    public class EntityClass
    {
        private IEntityRepository _entityRepository;
        public EntityClass(IEntityRepository entityRepository)
        {
            this._entityRepository = entityRepository;
        }
        public string Name { get; set; }
        public string GetNameWithPrefix(int id)
        {
            string name = string.Empty;
            if (id > 0)
            {
                name = this._entityRepository.GetName(id);
            }
            return "Mr. " + name;
        }
    }
    public interface IEntityRepository
    {
        string GetName(int id);
    }
    public class EntityRepository:IEntityRepository
    {
        public string GetName(int id)
        {
            // Code to connect to DB and get name based on Id
            return "NameFromDb";
        }
    }
}
Adarsh ​​Shah
sumber
4

Sudut pandang pengujian rintisan dan Mock:

  • Stub adalah implementasi dummy yang dilakukan oleh pengguna dengan cara statis yaitu dalam tulisan rintisan kode implementasi. Jadi tidak bisa menangani definisi layanan dan kondisi dinamis, Biasanya ini dilakukan dalam kerangka JUnit tanpa menggunakan kerangka kerja mocking.

  • Mock juga merupakan implementasi tiruan tetapi implementasinya dilakukan secara dinamis dengan menggunakan kerangka kerja Mocking seperti Mockito. Jadi kita dapat menangani definisi kondisi dan layanan sebagai cara dinamis yaitu tiruan dapat dibuat secara dinamis dari kode saat runtime. Jadi menggunakan tiruan kita bisa mengimplementasikan Rintisan bertopik secara dinamis.

Premraj
sumber
3

Ditambah jawaban yang bermanfaat, Salah satu poin paling kuat dalam menggunakan Mock daripada Subs

Jika kolaborator [yang menjadi sandaran kode utama] tidak berada di bawah kendali kami (mis. Dari perpustakaan pihak ketiga),
Dalam hal ini, rintisan lebih sulit untuk ditulis daripada dihina .

ahmednabil88
sumber
2

Saya telah menggunakan contoh python dalam jawaban saya untuk menggambarkan perbedaan.

Stub - Stubbing adalah teknik pengembangan perangkat lunak yang digunakan untuk mengimplementasikan metode kelas di awal siklus pengembangan. Mereka digunakan secara umum sebagai pengganti untuk implementasi dari antarmuka yang dikenal, di mana antarmuka tersebut diselesaikan atau diketahui tetapi implementasinya belum diketahui atau diselesaikan. Anda mulai dengan bertopik, yang berarti bahwa Anda hanya menuliskan definisi fungsi ke bawah dan meninggalkan kode aktual untuk nanti. Keuntungannya adalah Anda tidak akan melupakan metode dan Anda dapat terus memikirkan desain Anda sambil melihatnya dalam kode. Anda juga dapat meminta rintisan mengembalikan respons statis sehingga respons tersebut dapat digunakan oleh bagian lain kode Anda dengan segera. Objek rintisan memberikan respons yang valid, tetapi statis apa pun input yang Anda berikan, Anda akan selalu mendapatkan respons yang sama:

class Foo(object):
    def bar1(self):
        pass

    def bar2(self):
        #or ...
        raise NotImplementedError

    def bar3(self):
        #or return dummy data
        return "Dummy Data"

Mengejek objek yang digunakan dalam kasus-kasus uji mengejek mereka memvalidasi bahwa metode tertentu disebut pada objek tersebut. Objek tiruan adalah objek simulasi yang meniru perilaku objek nyata dengan cara yang terkontrol. Anda biasanya membuat objek tiruan untuk menguji perilaku beberapa objek lain. Mock memungkinkan kami mensimulasikan sumber daya yang tidak tersedia atau terlalu sulit untuk pengujian unit.

mymodule.py:

import os
import os.path

def rm(filename):
    if os.path.isfile(filename):
        os.remove(filename)

test.py:

from mymodule import rm
import mock
import unittest

class RmTestCase(unittest.TestCase):
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os):
        rm("any path")
        # test that rm called os.remove with the right parameters
        mock_os.remove.assert_called_with("any path")

if __name__ == '__main__':
    unittest.main()

Ini adalah contoh yang sangat mendasar yang hanya menjalankan rm dan menegaskan parameter yang dipanggil dengannya. Anda dapat menggunakan tiruan dengan objek tidak hanya berfungsi seperti yang ditunjukkan di sini, dan Anda juga dapat mengembalikan nilai sehingga objek tiruan dapat digunakan untuk menggantikan rintisan untuk pengujian.

Lebih lanjut tentang unittest.mock , note di python 2.x mock tidak termasuk dalam unittest tetapi merupakan modul yang dapat diunduh yang dapat diunduh melalui pip (pip install mock).

Saya juga telah membaca "The Art of Unit Testing" oleh Roy Osherove dan saya pikir akan lebih baik jika buku serupa ditulis menggunakan contoh Python dan Python. Jika ada yang tahu tentang buku seperti itu, silakan berbagi. Bersulang :)

radtek
sumber
2

Rintisan adalah benda palsu yang dibangun untuk tujuan pengujian. Mock adalah sebuah rintisan yang merekam apakah panggilan yang diharapkan terjadi secara efektif.

simon.denel
sumber
2

Stub adalah fungsi kosong yang digunakan untuk menghindari pengecualian yang tidak ditangani selama pengujian:

function foo(){}

Mock adalah fungsi buatan yang digunakan untuk menghindari ketergantungan OS, lingkungan atau perangkat keras selama pengujian:

function foo(bar){ window = this; return window.toString(bar); }

Dalam hal asersi dan negara:

  • Mengejek ditegaskan sebelum suatu peristiwa atau keadaan berubah
  • Rintisan bertopik tidak dinyatakan, mereka memberikan keadaan sebelum acara untuk menghindari mengeksekusi kode dari unit yang tidak terkait
  • Mata-mata diatur seperti bertopik, kemudian dinyatakan setelah suatu acara atau perubahan status
  • Palsu tidak ditegaskan, mereka berjalan setelah acara dengan dependensi hardcoded untuk menghindari keadaan

Referensi

Paul Sweatte
sumber
2
+1 untuk menambahkan mata-mata ke glosarium. Juga, saya pikir maksud Anda "Mata-mata diatur seperti tiruan" bukan "Mata-mata diatur seperti bertopik"
Sameh Deabes
2

Mock adalah objek teknis dan fungsional .

Mock itu teknis . Ini memang dibuat oleh perpustakaan mengejek (EasyMock, JMockit dan yang lebih baru-baru ini dikenal dengan Mockito) berkat pembuatan kode byte .
Implementasi tiruan dihasilkan dengan cara di mana kita bisa instrumen itu untuk mengembalikan nilai tertentu ketika suatu metode dipanggil tetapi juga beberapa hal lain seperti memverifikasi bahwa metode tiruan itu dipanggil dengan beberapa parameter tertentu (pemeriksaan ketat) atau apa pun parameternya ( tidak ada pemeriksaan ketat).

Membuat contoh tiruan:

@Mock Foo fooMock

Merekam perilaku:

when(fooMock.hello()).thenReturn("hello you!");

Memverifikasi permohonan:

verify(fooMock).hello()

Ini jelas bukan cara alami untuk membuat instance / menimpa kelas / perilaku Foo. Itu sebabnya saya merujuk pada aspek teknis.

Tetapi tiruannya juga fungsional karena ini adalah instance dari kelas yang perlu kita isolasi dari SUT. Dan dengan perilaku yang direkam, kita bisa menggunakannya dalam SUT dengan cara yang sama daripada yang kita lakukan dengan rintisan.


Stub hanyalah sebuah objek fungsional : itu adalah instance dari kelas yang perlu kita isolasi dari SUT dan itu saja. Itu berarti bahwa baik kelas rintisan dan semua perlengkapan perilaku yang diperlukan selama tes unit kami harus didefinisikan secara eksplisit.
Sebagai contoh, rintisan hello()perlu mensubclass Fookelas (atau mengimplementasikan antarmuka yang dimilikinya) dan menimpa hello() :

public class HelloStub extends Hello{    
  public String hello { 
      return "hello you!"; 
  }
}

Jika skenario pengujian lain membutuhkan pengembalian nilai lain, kita mungkin perlu menentukan cara umum untuk mengatur pengembalian:

public class HelloStub extends Hello{    
  public HelloStub(String helloReturn){
       this.helloReturn = helloReturn;
  }
  public String hello { 
      return helloReturn; 
  }
}

Skenario lain: jika saya memiliki metode efek samping (tidak ada pengembalian) dan saya akan memeriksa bahwa metode itu dipanggil, saya mungkin harus menambahkan boolean atau penghitung di kelas rintisan untuk menghitung berapa kali metode itu dipanggil.


Kesimpulan

Rintisan membutuhkan sering banyak overhead / kode untuk menulis untuk pengujian unit Anda. Mock apa yang mencegah berkat menyediakan fitur perekaman / verifikasi di luar kotak.
Itu sebabnya saat ini, pendekatan rintisan jarang digunakan dalam praktik dengan munculnya perpustakaan tiruan yang sangat baik.


Tentang Artikel Martin Fowler: Saya tidak berpikir untuk menjadi programmer "mockist" ketika saya menggunakan mock dan saya menghindari bertopik.
Tapi saya menggunakan tiruan ketika itu benar-benar diperlukan (dependensi yang mengganggu) dan saya lebih suka tes slicing dan mini-integrasi ketika saya menguji kelas dengan dependensi yang mengejek akan menjadi overhead.

davidxxx
sumber