Verspottet, stummelt und spioniert im Spock Framework

Spock bietet 3 leistungsstarke (aber im Wesentlichen unterschiedliche) Tools, die das Schreiben von Tests vereinfachen: Mock, Stub und Spy.



Sehr oft muss der Code, der getestet werden muss, mit externen Modulen interagieren, die als Abhängigkeiten bezeichnet werden (der ursprüngliche Artikel verwendet den Begriff Kollaborateure, der in der russischsprachigen Umgebung nicht sehr häufig vorkommt).


Unit-Tests dienen meistens dazu, eine einzelne isolierte Klasse mit verschiedenen Varianten von mok zu testen: Mock, Stub und Spy. Daher sind die Tests zuverlässiger und brechen weniger wahrscheinlich, wenn sich der Abhängigkeitscode weiterentwickelt.


Solche isolierten Tests sind weniger anfällig für Probleme, wenn die internen Details der Abhängigkeitsimplementierung geändert werden.


Von einem Übersetzer: Jedes Mal, wenn ich das Spock Framework zum Schreiben von Tests verwende, kann ich einen Fehler machen, wenn ich einen Weg zum Ersetzen von Abhängigkeiten wähle. Dieser Artikel enthält den kürzestmöglichen Spickzettel für die Auswahl eines Mechanismus zum Erstellen von Mokas.


TL; DR


Verspottet


Verwenden Sie Mock, um:


  • Vertragsprüfungen zwischen Testcode und Abhängigkeiten
  • Überprüfen, ob Abhängigkeitsmethoden die richtige Anzahl von Malen aufgerufen werden
  • Validierung von Parametern, mit denen der Abhängigkeitscode aufgerufen wird

Stubs


Verwenden Sie Stub, um:


  • Bereitstellung vordefinierter Anrufergebnisse
  • Ausführen vordefinierter Aktionen, die von Abhängigkeiten erwartet werden, z. B. Auslösen von Ausnahmen

Spione


Angst Spione. Wie die Spock-Dokumentation sagt:


Überlegen Sie zweimal, bevor Sie diesen Mechanismus verwenden. Vielleicht sollten Sie Ihre Lösung neu gestalten und Ihren Code neu organisieren.

Aber es kommt einfach so vor, dass es Situationen gibt, in denen wir mit Legacy-Code arbeiten müssen. Es kann schwierig oder sogar unmöglich sein, Legacy-Code mit Mobs und Stubs zu testen. In diesem Fall gibt es nur eine Lösung: Verwenden Sie Spy.


Es ist besser, Legacy-Code mit Tests mit Spy zu behandeln, als überhaupt keine Legacy-Tests durchzuführen.


Verwenden Sie Spy, um:


  • Testen von Legacy-Code, der nicht mit anderen Methoden getestet werden kann
  • Überprüfen, ob Abhängigkeitsmethoden die richtige Anzahl von Malen aufgerufen werden
  • Validierung übergebener Parameter
  • Bereitstellen einer vordefinierten Abhängigkeitsantwort
  • Ausführen vordefinierter Aktionen als Reaktion auf Aufrufe von Abhängigkeitsmethoden

Verspottet



Die ganze Kraft von Moxas zeigt sich, wenn die Aufgabe eines Komponententests darin besteht, den Vertrag zwischen dem getesteten Code und den Abhängigkeiten zu überprüfen. Schauen wir uns das folgende Beispiel an, in dem wir einen FooController Controller haben, der FooService als Abhängigkeit verwendet, und testen Sie diese Funktionalität mit Mocks.


FooController.groovy


 package com.mycompany.myapp import groovy.transform.CompileStatic @CompileStatic class FooController { FooService fooService def doSomething() { render fooService.doSomething("Sally") } } 

FooService.groovy


 package com.mycompany.myapp import groovy.transform.CompileStatic @CompileStatic class FooService { String doSomething(String name) { "Hi ${name}, FooService did something" } } 

In diesem Szenario möchten wir einen Test schreiben, der Folgendes testet:


  • Vertrag zwischen FooController und FooService
  • FooService.doSomething(name) wird die richtige Anzahl von Malen aufgerufen
  • FooService.doSomething(name) wird mit dem richtigen Parameter aufgerufen

Schauen Sie sich den Test an:


MockSpec.groovy


 package com.mycompany.myapp import grails.testing.web.controllers.ControllerUnitTest import spock.lang.Specification class MockSpec extends Specification implements ControllerUnitTest<FooController> { void "Mock FooService"() { given: "  " def fooService = Mock(FooService) and: "    " controller.fooService = fooService when: "  " controller.doSomething() then: "         " 1 * fooService.doSomething("Sally") and: "  ''    - 'null'" response.text == null.toString() } } 

Der obige Test erstellt ein Service-Modell:


 def fooService = Mock(FooService) 

Der Test prüft auch, ob FooService.doSomething(name) einmal aufgerufen wird und der an ihn übergebene Parameter mit der Zeichenfolge "Sally" übereinstimmt.


 1 * fooService.doSomething("Sally") 

Der obige Code löst 4 wichtige Probleme:


  • erstellt Mock für FooService
  • überprüft, FooService.doSomething(String name) genau einmal mit dem String Parameter und dem Wert "Sally" aufgerufen wird.
  • isoliert den Testcode und ersetzt die Abhängigkeitsimplementierung

Stubs


Verwendet der getestete Code Abhängigkeiten? Ist der Zweck des Testens, um sicherzustellen, dass der zu testende Code bei der Interaktion mit Abhängigkeiten ordnungsgemäß funktioniert? Geben die Ergebnisse von Aufrufen von Abhängigkeitsmethoden Eingabewerte für den zu testenden Code ein?


Wenn sich das Verhalten des getesteten Codes abhängig vom Verhalten der Abhängigkeiten ändert, müssen Sie Stubs (Stub) verwenden.


Schauen wir uns das folgende Beispiel mit FooController und FooService und testen die Controller-Funktionalität mithilfe von Stubs.


FooController.groovy


 package com.mycompany.myapp import groovy.transform.CompileStatic @CompileStatic class FooController { FooService fooService def doSomething() { render fooService.doSomething("Sally") } } 

FooService.groovy


 package com.mycompany.myapp import groovy.transform.CompileStatic @CompileStatic class FooService { String doSomething(String name) { "Hi ${name}, FooService did something" } } 

Testcode:


StubSpec.groovy


 package com.mycompany.myapp import grails.testing.web.controllers.ControllerUnitTest import spock.lang.Specification class StubSpec extends Specification implements ControllerUnitTest<FooController> { void "Stub FooService"() { given: "  " def fooService = Stub(FooService) { doSomething(_) >> "Stub did something" } and: "    " controller.fooService = fooService when: "  " controller.doSomething() then: "   " // 1 * fooService.doSomething() //        response.text == "Stub did something" } } 

Sie können einen Stub wie folgt erstellen:


 def fooService = Stub(FooService) { doSomething(_) >> "Stub did something" } 

Der obige Code löst 4 wichtige Probleme:


  • erstellt einen Stub FooService
  • stellt sicher, dass FooService.doSomething(String name) den String "Stub did something" FooService.doSomething(String name) unabhängig vom übergebenen Parameter (also haben wir das Zeichen _ verwendet)
  • isoliert den getesteten Code und ersetzt die Abhängigkeitsimplementierung durch einen Stub

Spione


Bitte lesen Sie diesen Abschnitt nicht.


Schau nicht hin.


Fahren Sie mit dem nächsten fort.


Lesen Sie noch? Na dann, okay, lass uns mit Spy umgehen.



Verwenden Sie nicht Spy. Wie die Spock-Dokumentation sagt:


Überlegen Sie zweimal, bevor Sie diesen Mechanismus verwenden. Vielleicht sollten Sie Ihre Lösung neu gestalten und Ihren Code neu organisieren.

In diesem Fall gibt es Situationen, in denen wir mit Legacy-Code arbeiten müssen. Legacy-Code kann nicht mit Mobs oder Stubs getestet werden. In diesem Fall bleibt der Spion die einzig gangbare Option.


Spione unterscheiden sich von Mokas oder Stubs, weil sie nicht wie Stubs funktionieren.


Wenn eine Abhängigkeit durch einen Mok oder Stub ersetzt wird, wird ein Testobjekt erstellt und der tatsächliche Quellcode der Abhängigkeit wird nicht ausgeführt.


Der Spion hingegen führt den Hauptquellcode der Abhängigkeit aus, für die der Spion erstellt wurde. Mit dem Spion können Sie jedoch ändern, was der Spion zurückgibt, und Methodenaufrufe überprüfen, genau wie bei Mokas und Stubs. (Daher der Name Spion).


Schauen wir uns das folgende FooController Beispiel an, das FooService , und testen Sie dann die Funktionalität mit einem Spion.


FooController.groovy


 package com.mycompany.myapp import groovy.transform.CompileStatic @CompileStatic class FooController { FooService fooService def doSomething() { render fooService.doSomething("Sally") } } 

FooService.groovy


 package com.mycompany.myapp import groovy.transform.CompileStatic @CompileStatic class FooService { String doSomething(String name) { "Hi ${name}, FooService did something" } } 

Testcode:


SpySpec.groovy


 package com.mycompany.myapp import grails.testing.web.controllers.ControllerUnitTest import spock.lang.Specification class SpySpec extends Specification implements ControllerUnitTest<FooController> { void "Spy FooService"() { given: " -" def fooService = Spy(FooService) and: "   " controller.fooService = fooService when: "  " controller.doSomething() then: "     " 1 * fooService.doSomething("Sally") >> "A Spy can modify implementation" and: '     ' response.text == "A Spy can modify implementation" } } 

Das Erstellen einer Spionageinstanz ist ganz einfach:


 def fooService = Spy(FooService) 

Im obigen Code können wir mit dem Spion den Aufruf von FooService.doSomething(name) , die Anzahl der Aufrufe und Parameterwerte überprüfen. Darüber hinaus ändert der Spion die Implementierung der Methode, um einen anderen Wert zurückzugeben.


 1 * fooService.doSomething("Sally") >> "A Spy can modify implementation" 

Der obige Code löst 4 wichtige Probleme:


  • FooService eine Spionageinstanz für FooService
  • Überprüft die Abhängigkeitsinteraktion
  • Überprüft, wie die Anwendung gemäß bestimmten Ergebnissen von Abhängigkeitsmethodenaufrufen funktioniert
  • isoliert den getesteten Code und ersetzt die Abhängigkeitsimplementierung durch einen Stub

FAQ


Welche Option verwenden: Mock, Stub oder Spy?


Dies ist eine Frage, mit der viele Entwickler konfrontiert sind. Diese FAQ kann hilfreich sein, wenn Sie sich nicht sicher sind, welchen Ansatz Sie verwenden sollen.


F: Ist der Zweck des Testens der Überprüfung des Vertrags zwischen dem getesteten Code und den Abhängigkeiten?


A: Wenn Sie mit Ja geantwortet haben, verwenden Sie Mock


F: Ist der Zweck des Testens, um sicherzustellen, dass der zu testende Code bei der Interaktion mit Abhängigkeiten ordnungsgemäß funktioniert?


A: Wenn Sie mit Ja geantwortet haben, verwenden Sie Stub


F: Sind die Ergebnisse des Aufrufs von Abhängigkeitsmethoden Eingabewerte für den zu testenden Code?


A: Wenn Sie mit Ja geantwortet haben, verwenden Sie Stub


F: Arbeiten Sie mit Legacy-Code, der sehr schwer zu testen ist, und Sie haben keine Optionen mehr?


A: Versuchen Sie es mit Spy


Codebeispiel


Den Code für alle Beispiele in diesem Artikel finden Sie unter:


https://github.com/ddelponte/mock-stub-spy


Nützliche Links


Source: https://habr.com/ru/post/de435488/


All Articles