Saya tidak mengerti perbedaan antara Mock, Stub, dan Spy dalam pengujian Spock dan tutorial yang saya lihat online tidak menjelaskannya secara rinci.
Saya tidak mengerti perbedaan antara Mock, Stub, dan Spy dalam pengujian Spock dan tutorial yang saya lihat online tidak menjelaskannya secara rinci.
Jawaban:
Perhatian: Saya akan menyederhanakan dan bahkan mungkin sedikit memalsukannya di paragraf yang akan datang. Untuk info lebih detail lihat situs web Martin Fowler .
Mock adalah kelas dummy menggantikan yang asli, mengembalikan sesuatu seperti null atau 0 untuk setiap panggilan metode. Anda menggunakan tiruan jika Anda memerlukan contoh dummy dari kelas kompleks yang jika tidak menggunakan sumber daya eksternal seperti koneksi jaringan, file atau database atau mungkin menggunakan lusinan objek lainnya. Keuntungan dari tiruan adalah Anda dapat mengisolasi kelas yang diuji dari seluruh sistem.
Stub juga merupakan kelas dummy yang memberikan beberapa hasil yang lebih spesifik, disiapkan atau direkam sebelumnya, dan diputar ulang untuk permintaan tertentu yang sedang diuji. Bisa dibilang stub adalah tiruan yang mewah. Di Spock Anda akan sering membaca tentang metode rintisan.
Mata-mata adalah sejenis hibrida antara objek nyata dan rintisan, yaitu pada dasarnya adalah objek nyata dengan beberapa (tidak semua) metode yang dibayangi oleh metode rintisan. Metode non-stub hanya diarahkan ke objek aslinya. Dengan cara ini Anda dapat memiliki perilaku asli untuk metode "murah" atau sepele dan perilaku palsu untuk metode "mahal" atau kompleks.
Pembaruan 2017-02-06: Sebenarnya jawaban mikhail pengguna lebih spesifik untuk Spock daripada yang asli saya di atas. Jadi dalam lingkup Spock, apa yang dia gambarkan benar, tapi itu tidak memalsukan jawaban umum saya:
Sekarang di sini adalah contoh pengujian yang dapat dieksekusi, mendemonstrasikan apa yang mungkin dan apa yang tidak. Ini sedikit lebih instruktif daripada cuplikan mikhail. Terima kasih banyak kepadanya karena telah menginspirasi saya untuk meningkatkan jawaban saya sendiri! :-)
package de.scrum_master.stackoverflow
import org.spockframework.mock.TooFewInvocationsError
import org.spockframework.runtime.InvalidSpecException
import spock.lang.FailsWith
import spock.lang.Specification
class MockStubSpyTest extends Specification {
static class Publisher {
List<Subscriber> subscribers = new ArrayList<>()
void addSubscriber(Subscriber subscriber) {
subscribers.add(subscriber)
}
void send(String message) {
for (Subscriber subscriber : subscribers)
subscriber.receive(message);
}
}
static interface Subscriber {
String receive(String message)
}
static class MySubscriber implements Subscriber {
@Override
String receive(String message) {
if (message ==~ /[A-Za-z ]+/)
return "ok"
return "uh-oh"
}
}
Subscriber realSubscriber1 = new MySubscriber()
Subscriber realSubscriber2 = new MySubscriber()
Publisher publisher = new Publisher(subscribers: [realSubscriber1, realSubscriber2])
def "Real objects can be tested normally"() {
expect:
realSubscriber1.receive("Hello subscribers") == "ok"
realSubscriber1.receive("Anyone there?") == "uh-oh"
}
@FailsWith(TooFewInvocationsError)
def "Real objects cannot have interactions"() {
when:
publisher.send("Hello subscribers")
publisher.send("Anyone there?")
then:
2 * realSubscriber1.receive(_)
}
def "Stubs can simulate behaviour"() {
given:
def stubSubscriber = Stub(Subscriber) {
receive(_) >>> ["hey", "ho"]
}
expect:
stubSubscriber.receive("Hello subscribers") == "hey"
stubSubscriber.receive("Anyone there?") == "ho"
stubSubscriber.receive("What else?") == "ho"
}
@FailsWith(InvalidSpecException)
def "Stubs cannot have interactions"() {
given: "stubbed subscriber registered with publisher"
def stubSubscriber = Stub(Subscriber) {
receive(_) >> "hey"
}
publisher.addSubscriber(stubSubscriber)
when:
publisher.send("Hello subscribers")
publisher.send("Anyone there?")
then:
2 * stubSubscriber.receive(_)
}
def "Mocks can simulate behaviour and have interactions"() {
given:
def mockSubscriber = Mock(Subscriber) {
3 * receive(_) >>> ["hey", "ho"]
}
publisher.addSubscriber(mockSubscriber)
when:
publisher.send("Hello subscribers")
publisher.send("Anyone there?")
then: "check interactions"
1 * mockSubscriber.receive("Hello subscribers")
1 * mockSubscriber.receive("Anyone there?")
and: "check behaviour exactly 3 times"
mockSubscriber.receive("foo") == "hey"
mockSubscriber.receive("bar") == "ho"
mockSubscriber.receive("zot") == "ho"
}
def "Spies can have interactions"() {
given:
def spySubscriber = Spy(MySubscriber)
publisher.addSubscriber(spySubscriber)
when:
publisher.send("Hello subscribers")
publisher.send("Anyone there?")
then: "check interactions"
1 * spySubscriber.receive("Hello subscribers")
1 * spySubscriber.receive("Anyone there?")
and: "check behaviour for real object (a spy is not a mock!)"
spySubscriber.receive("Hello subscribers") == "ok"
spySubscriber.receive("Anyone there?") == "uh-oh"
}
def "Spies can modify behaviour and have interactions"() {
given:
def spyPublisher = Spy(Publisher) {
send(_) >> { String message -> callRealMethodWithArgs("#" + message) }
}
def mockSubscriber = Mock(MySubscriber)
spyPublisher.addSubscriber(mockSubscriber)
when:
spyPublisher.send("Hello subscribers")
spyPublisher.send("Anyone there?")
then: "check interactions"
1 * mockSubscriber.receive("#Hello subscribers")
1 * mockSubscriber.receive("#Anyone there?")
}
}
Pertanyaannya adalah dalam konteks kerangka kerja Spock dan saya tidak percaya jawaban saat ini memperhitungkan hal ini.
Berdasarkan dokumen Spock (contoh disesuaikan, kata-kata saya sendiri ditambahkan):
Stub: Digunakan untuk membuat kolaborator menanggapi panggilan metode dengan cara tertentu. Saat menghentikan suatu metode, Anda tidak peduli apakah dan berapa kali metode tersebut akan dipanggil; Anda hanya ingin mengembalikan nilai, atau melakukan beberapa efek samping, setiap kali dipanggil.
subscriber.receive(_) >> "ok" // subscriber is a Stub()
Mock: Digunakan untuk menggambarkan interaksi antara objek di bawah spesifikasi dan kolaboratornya.
def "should send message to subscriber"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("hello") // subscriber is a Mock()
}
A Mock dapat bertindak sebagai Mock dan Stub:
1 * subscriber.receive("message1") >> "ok" // subscriber is a Mock()
Spy: Selalu didasarkan pada objek nyata dengan metode orisinal yang melakukan hal-hal nyata. Dapat digunakan seperti Stub untuk mengubah nilai kembalian dari metode tertentu. Dapat digunakan seperti Mock untuk menggambarkan interaksi.
def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
def "should send message to subscriber"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("message1") >> "ok" // subscriber is a Spy(), used as a Mock an Stub
}
def "should send message to subscriber (actually handle 'receive')"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("message1") // subscriber is a Spy(), used as a Mock, uses real 'receive' function
}
Ringkasan:
Hindari menggunakan Mock () jika Stub () sudah cukup.
Hindari menggunakan Spy () jika Anda bisa, karena harus melakukannya bisa jadi bau dan petunjuk tentang pengujian yang salah atau desain yang salah dari objek yang diuji.
Secara sederhana:
Mock: Anda mengejek suatu tipe dan dengan cepat Anda mendapatkan sebuah objek yang dibuat. Metode dalam objek tiruan ini mengembalikan nilai default tipe pengembalian.
Stub: Anda membuat kelas rintisan di mana metode didefinisikan ulang dengan definisi sesuai kebutuhan Anda. Contoh: Dalam metode objek nyata Anda memanggil dan api eksternal dan mengembalikan nama pengguna dan id. Dalam metode objek stub Anda mengembalikan beberapa nama dummy.
Spy: Anda membuat satu objek nyata dan kemudian Anda memata-matai. Sekarang Anda dapat mengejek beberapa metode dan memilih untuk tidak melakukannya untuk beberapa metode.
Satu perbedaan penggunaan adalah Anda tidak dapat meniru objek tingkat metode. sedangkan Anda dapat membuat objek default dalam metode dan kemudian memata-matai untuk mendapatkan perilaku metode yang diinginkan dalam objek mata-mata.
Rintisan sebenarnya hanya untuk memfasilitasi pengujian unit, mereka bukan bagian dari pengujian. Ejekan, adalah bagian dari ujian, bagian dari verifikasi, bagian dari lulus / gagal.
Jadi, katakanlah Anda memiliki metode yang mengambil objek sebagai parameter. Anda tidak pernah melakukan apa pun yang mengubah parameter ini dalam pengujian. Anda cukup membaca nilai darinya. Itu rintisan.
Jika Anda mengubah sesuatu, atau perlu memverifikasi semacam interaksi dengan objek, maka itu adalah tiruan.