testing - software - stub informatica
¿Alguien puede explicar la diferencia entre Mock, Stub y Spy en las pruebas de marco de Spock y cuándo usarlas? (3)
No entiendo la diferencia entre Mock, Stub y Spy en las pruebas de Spock y los tutoriales que he estado viendo en línea no los explican en detalle.
En lenguaje sencillo:
Mock: te burlas de un tipo y sobre la marcha obtienes un objeto creado. Los métodos en este objeto simulado devuelven los valores predeterminados del tipo de devolución.
Stub: crea una clase de stub donde los métodos se redefinen con definición según su requisito. Ej .: En el método de objeto real, usted llama y api externo y devuelve el nombre de usuario y la identificación. En el método de objetos trozados, devuelve un nombre ficticio.
Espía: creas un objeto real y luego lo espías. Ahora puedes burlarte de algunos métodos y elegir no hacerlo para algunos.
Una diferencia de uso es que no puede simular objetos de nivel de método. mientras que puede crear un objeto predeterminado en el método y luego espiarlo para obtener el comportamiento deseado de los métodos en el objeto espía.
La pregunta estaba en el contexto del marco de Spock y no creo que las respuestas actuales lo tengan en cuenta.
Basado en documentos de Spock (ejemplos personalizados, mi propia redacción agregada):
Stub: se utiliza para hacer que los colaboradores respondan a las llamadas a métodos de cierta manera. Al anular un método, no le importa si y cuántas veces se va a llamar al método; solo desea que devuelva algún valor, o que realice algún efecto secundario, cada vez que recibe una llamada.
subscriber.receive(_) >> "ok" // subscriber is a Stub()
Mock: se usa para describir las interacciones entre el objeto bajo especificación y sus colaboradores.
def "should send message to subscriber"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("hello") // subscriber is a Mock()
}
Un simulacro puede actuar como un simulacro y un talón:
1 * subscriber.receive("message1") >> "ok" // subscriber is a Mock()
Spy: siempre se basa en un objeto real con métodos originales que hacen cosas reales. Se puede usar como un Stub para cambiar los valores de retorno de los métodos de selección. Se puede usar como un simulacro para describir las interacciones.
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
}
Resumen:
- Un Stub () es un Stub.
- A Mock () es un Stub y Mock.
- Un Spy () es un Stub, Mock y Spy.
Evite usar Mock () si Stub () es suficiente.
Evite usar Spy () si puede, teniendo que hacerlo podría ser un olor y una prueba incorrecta o un diseño incorrecto del objeto bajo prueba.
Atención: voy a simplificar en exceso y tal vez incluso a falsificar un poco en los próximos párrafos. Para obtener información más detallada, visite el sitio web de Martin Fowler .
Un simulacro es una clase ficticia que reemplaza a una verdadera, devolviendo algo como nulo o 0 para cada llamada de método. Utiliza un simulacro si necesita una instancia ficticia de una clase compleja que, de lo contrario, usaría recursos externos, como conexiones de red, archivos o bases de datos, o tal vez use docenas de otros objetos. La ventaja de los simulacros es que puedes aislar la clase bajo prueba del resto del sistema.
Un apéndice también es una clase ficticia que proporciona resultados reproducidos más específicos, preparados o pregrabados para ciertas solicitudes bajo prueba. Se podría decir que un talón es un simulacro de fantasía. En Spock, a menudo lees sobre los métodos stub.
Un espía es una especie de híbrido entre el objeto real y el trozo, es decir, es básicamente el objeto real con algunos (no todos) los métodos sombreados por los métodos de trozo. Los métodos no codificados simplemente se envían al objeto original. De esta forma, puede tener un comportamiento original para métodos "baratos" o triviales y un comportamiento falso para métodos "caros" o complejos.
Actualización 2017-02-06: en realidad, la respuesta de mikhail del usuario es más específica para Spock que la original anterior. Entonces, dentro del alcance de Spock, lo que él describe es correcto, pero eso no falsifica mi respuesta general:
- Un trozo se refiere a la simulación de un comportamiento específico. En Spock, esto es todo lo que un trozo puede hacer, por lo que es algo muy simple.
- Un simulacro se refiere a un sustituto de un objeto real (posiblemente caro), proporcionando respuestas no operativas para todas las llamadas a métodos. En este sentido, un simulacro es más simple que un talón. Pero en Spock, un simulacro también puede dar lugar a resultados del método, es decir, ser un simulacro y un talón. Además, en Spock podemos contar con qué frecuencia se han invocado métodos simulados específicos con ciertos parámetros durante una prueba.
- Un espía siempre envuelve un objeto real y, por defecto, enruta todas las llamadas al objeto original, pasando también por los resultados originales. El método de conteo de llamadas también funciona para espías. En Spock, un espía también puede modificar el comportamiento del objeto original, manipulando los parámetros de llamada de método y / o los resultados o bloqueando los métodos originales para que no sean llamados.
Ahora aquí hay una prueba de ejemplo ejecutable, que demuestra lo que es posible y lo que no. Es un poco más instructivo que los fragmentos de mikhail. ¡Muchas gracias a él por inspirarme a mejorar mi propia respuesta! :-)
package de.scrum_master.
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?")
}
}