Saltar al contenido

Diferencia entre Mock / Stub / Spy en el marco de prueba de Spock

Te damos la bienvenida a proyecto online, ahora hallarás la respuesta que buscas.

Solución:

Atención: voy a simplificar demasiado y tal vez incluso falsificar ligeramente en los próximos párrafos. Para obtener información más detallada, consulte el sitio web de Martin Fowler.

Un simulacro es una clase ficticia que reemplaza a una real, devolviendo algo como null o 0 para cada llamada al método. Utilice un simulacro si necesita una instancia ficticia de una clase compleja que de otra manera usaría recursos externos como conexiones de red, archivos o bases de datos o tal vez usaría docenas de otros objetos. La ventaja de los simulacros es que puede aislar la clase bajo prueba del resto del sistema.

Un stub es también una clase ficticia que proporciona resultados reproducidos más específicos, preparados o pregrabados para determinadas solicitudes bajo prueba. Se podría decir que un talón es una burla elegante. En Spock, a menudo leerá sobre métodos de código auxiliar.

Un espía es una especie de híbrido entre un objeto real y un código auxiliar, es decir, es básicamente el objeto real con algunos métodos (no todos) sombreados por métodos auxiliares. Los métodos no aptos se enrutan simplemente al objeto original. De esta forma, puede tener un comportamiento original para los métodos “baratos” o triviales y un comportamiento falso para los métodos “costosos” o complejos.


Actualización 2017-02-06: En realidad, la respuesta del usuario mikhail es más específica para Spock que la original anterior. Entonces, dentro del alcance de Spock, lo que describe es correcto, pero eso no falsifica mi respuesta general:

  • Un stub se ocupa de simular un comportamiento específico. En Spock, esto es todo lo que puede hacer un stub, por lo que es lo más simple.
  • Un simulacro se ocupa de reemplazar un objeto real (posiblemente costoso), 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 eliminar los resultados del método, es decir, ser un simulacro y un talón. Además, en Spock podemos contar con qué frecuencia se han llamado 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 método al objeto original, pasando también por los resultados originales. El recuento de llamadas de método 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 al 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.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 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?")
  

La pregunta estaba en el contexto del marco de Spock y no creo que las respuestas actuales tomen esto en cuenta.

Basado en documentos de Spock (ejemplos personalizados, mi propia redacción agregada):

Talón:Se usa para hacer que los colaboradores respondan a las llamadas a métodos de cierta manera. Al apuntar un método, no le importa si se va a llamar al método ni cuántas veces; solo desea que devuelva algún valor, o realice algún efecto secundario, siempre que sea llamado.

subscriber.receive(_) >> "ok" // subscriber is a Stub()

Burlarse de:Se usa para describir 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 Mock puede actuar como Mock y Stub:

1 * subscriber.receive("message1") >> "ok" // subscriber is a Mock()

Espiar: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 seleccionados. Puede usarse como un simulacro para describir 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.
  • Un Mock () es un Stub y un Mock.
  • Un espía () es un código auxiliar, un simulacro y un espía.

Evite usar Mock () si Stub () es suficiente.

Evite usar Spy () si puede, tener que hacerlo podría ser un olor y pistas de una prueba incorrecta o un diseño incorrecto del objeto bajo prueba.

En lenguaje sencillo:

Simulacro: te burlas de un tipo y sobre la marcha obtienes un objeto creado. Los métodos de este objeto simulado devuelven los valores predeterminados del tipo de retorno.

Stub: crea una clase de stub donde los métodos se redefinen con la definición de acuerdo con sus requisitos. Por ejemplo: en el método de objeto real, llama a una API externa y devuelve el nombre de usuario contra y la identificación. En el método de objeto stubbed devuelve un nombre ficticio.

Espía: Creas un objeto real y luego lo espías. Ahora puede burlarse de algunos métodos y optar por no hacerlo para algunos.

Una diferencia de uso es 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 espiado.

Calificaciones y comentarios

Recuerda algo, que tienes permiso de valorar este tutorial si te fue preciso.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *