Ich verstehe den Unterschied zwischen Mock, Stub und Spy beim Spock-Testen nicht und die Tutorials, die ich mir online angesehen habe, erklären sie nicht im Detail.
Ich verstehe den Unterschied zwischen Mock, Stub und Spy beim Spock-Testen nicht und die Tutorials, die ich mir online angesehen habe, erklären sie nicht im Detail.
Antworten:
Achtung: Ich werde in den kommenden Absätzen zu stark vereinfachen und vielleicht sogar leicht verfälschen. Weitere Informationen finden Sie auf der Website von Martin Fowler .
Ein Mock ist eine Dummy-Klasse, die eine echte ersetzt und für jeden Methodenaufruf so etwas wie null oder 0 zurückgibt. Sie verwenden ein Modell, wenn Sie eine Dummy-Instanz einer komplexen Klasse benötigen, die andernfalls externe Ressourcen wie Netzwerkverbindungen, Dateien oder Datenbanken oder möglicherweise Dutzende anderer Objekte verwenden würde. Der Vorteil von Mocks besteht darin, dass Sie die zu testende Klasse vom Rest des Systems isolieren können.
Ein Stub ist auch eine Dummy-Klasse, die einige spezifischere, vorbereitete oder aufgezeichnete, wiedergegebene Ergebnisse für bestimmte zu testende Anforderungen liefert. Man könnte sagen, ein Stummel ist ein schickes Mock. In Spock werden Sie häufig über Stub-Methoden lesen.
Ein Spion ist eine Art Hybrid zwischen realem Objekt und Stub, dh es ist im Grunde das reale Objekt mit einigen (nicht allen) Methoden, die durch Stub-Methoden beschattet werden. Nicht gestubbte Methoden werden nur zum ursprünglichen Objekt weitergeleitet. Auf diese Weise können Sie ursprüngliches Verhalten für "billige" oder triviale Methoden und falsches Verhalten für "teure" oder komplexe Methoden haben.
Update 2017-02-06: Tatsächlich ist die Antwort von Benutzer mikhail spezifischer für Spock als meine ursprüngliche Antwort oben. Im Rahmen von Spock ist das, was er beschreibt, richtig, aber das verfälscht meine allgemeine Antwort nicht:
Hier ist ein ausführbarer Beispieltest, der zeigt, was möglich ist und was nicht. Es ist etwas lehrreicher als Mikhails Schnipsel. Vielen Dank an ihn, dass er mich inspiriert hat, meine eigene Antwort zu verbessern! :-)
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?")
}
}
Die Frage stand im Zusammenhang mit dem Spock-Framework und ich glaube nicht, dass die aktuellen Antworten dies berücksichtigen.
Basierend auf Spock-Dokumenten (Beispiele angepasst, mein eigener Wortlaut hinzugefügt):
Stub: Wird verwendet, um Mitarbeiter dazu zu bringen, auf Methodenaufrufe auf eine bestimmte Weise zu reagieren. Wenn Sie eine Methode stubben, ist es Ihnen egal, ob und wie oft die Methode aufgerufen wird. Sie möchten nur, dass es einen Wert zurückgibt oder einen Nebeneffekt ausführt, wenn es aufgerufen wird.
subscriber.receive(_) >> "ok" // subscriber is a Stub()
Mock: Wird verwendet, um Interaktionen zwischen dem zu spezifizierenden Objekt und seinen Mitarbeitern zu beschreiben.
def "should send message to subscriber"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("hello") // subscriber is a Mock()
}
Ein Mock kann als Mock und Stub fungieren:
1 * subscriber.receive("message1") >> "ok" // subscriber is a Mock()
Spion: Basiert immer auf einem realen Objekt mit originellen Methoden, die reale Dinge tun. Kann wie ein Stub verwendet werden, um die Rückgabewerte ausgewählter Methoden zu ändern. Kann wie ein Mock verwendet werden, um Interaktionen zu beschreiben.
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
}
Zusammenfassung:
Vermeiden Sie die Verwendung von Mock (), wenn Stub () ausreicht.
Vermeiden Sie die Verwendung von Spy (), wenn Sie können. Dies kann ein Geruch sein und Hinweise auf einen falschen Test oder ein falsches Design des zu testenden Objekts geben.
In einfachen Worten:
Mock: Sie verspotten einen Typ und erhalten im Handumdrehen ein Objekt erstellt. Methoden in diesem Scheinobjekt geben die Standardwerte des Rückgabetyps zurück.
Stub: Sie erstellen eine Stub-Klasse, in der Methoden gemäß Ihren Anforderungen mit der Definition neu definiert werden. Beispiel: In der Real-Object-Methode rufen Sie eine externe API auf und geben den Benutzernamen gegen und id zurück. In der Stubbed-Object-Methode geben Sie einen Dummy-Namen zurück.
Spion: Sie erstellen ein echtes Objekt und spionieren es dann aus. Jetzt können Sie einige Methoden verspotten und dies für einige nicht tun.
Ein Verwendungsunterschied besteht darin, dass Sie keine Objekte auf Methodenebene verspotten können. Sie können ein Standardobjekt in der Methode erstellen und es dann ausspionieren, um das gewünschte Verhalten der Methoden im ausspionierten Objekt zu erhalten.
Stubs dienen eigentlich nur zur Erleichterung des Unit-Tests, sie sind nicht Teil des Tests. Mocks sind Teil des Tests, Teil der Überprüfung, Teil des Bestehens / Nichtbestehens.
Angenommen, Sie haben eine Methode, die ein Objekt als Parameter verwendet. Sie tun niemals etwas, was diesen Parameter im Test ändert. Sie lesen einfach einen Wert daraus. Das ist ein Stummel.
Wenn Sie etwas ändern oder eine Interaktion mit dem Objekt überprüfen müssen, handelt es sich um eine Verspottung.