He leído varios artículos sobre burlarse y tropezar en las pruebas, incluidos Los simulacros de Martin Fowler no son trozos , pero aún no entiendo la diferencia.
He leído varios artículos sobre burlarse y tropezar en las pruebas, incluidos Los simulacros de Martin Fowler no son trozos , pero aún no entiendo la diferencia.
Respuestas:
Talón
Creo que la mayor distinción es que un trozo que ya has escrito con un comportamiento predeterminado. Por lo tanto, tendría una clase que implementa la dependencia (clase abstracta o interfaz más probable) que está falsificando para fines de prueba y los métodos simplemente se eliminarían con las respuestas establecidas. No harían nada lujoso y ya habría escrito el código tropezado fuera de su prueba.
Burlarse de
Un simulacro es algo que, como parte de su prueba, debe configurar con sus expectativas. Un simulacro no está configurado de manera predeterminada, por lo que tiene un código que lo hace en su prueba. En cierto modo, los simulacros se determinan en tiempo de ejecución ya que el código que establece las expectativas debe ejecutarse antes de que hagan algo.
Diferencia entre simulacros y trozos
Las pruebas escritas con simulacros generalmente siguen un initialize -> set expectations -> exercise -> verify
patrón de prueba. Mientras que el trozo preescrito seguiría uninitialize -> exercise -> verify
.
Similitud entre simulacros y trozos
El propósito de ambos es eliminar la prueba de todas las dependencias de una clase o función para que sus pruebas estén más enfocadas y más simples en lo que están tratando de probar.
Hay varias definiciones de objetos que no son reales. El término general es prueba doble . Este término abarca: ficticio , falso , trozo , simulacro .
Según el artículo de Martin Fowler :
- Los objetos ficticios se pasan pero nunca se usan realmente. Por lo general, solo se usan para completar listas de parámetros.
- Falso objetos realidad tienen implementaciones que funcionan, pero generalmente toman algún atajo que los hace no adecuados para la producción (una base de datos en memoria es un buen ejemplo).
- Trozos proporcionan respuestas enlatadas a las llamadas realizadas durante la prueba, por lo general no responden en absoluto a nada fuera de lo programado para la prueba. Los resguardos también pueden registrar información sobre llamadas, como un resguardo de puerta de enlace de correo electrónico que recuerda los mensajes que 'envió', o tal vez solo la cantidad de mensajes que 'envió'.
- Aquí estamos hablando de simulacros : objetos preprogramados con expectativas que forman una especificación de las llamadas que se espera que reciban.
Mocks vs Stubs = Pruebas de comportamiento vs Pruebas estatales
De acuerdo con el principio de Prueba, solo una cosa por prueba , puede haber varios trozos en una prueba, pero generalmente solo hay un simulacro.
Pruebe el ciclo de vida con trozos:
Pruebe el ciclo de vida con simulacros:
Tanto las pruebas simuladas como las de prueba dan una respuesta a la pregunta: ¿Cuál es el resultado?
Las pruebas con simulacros también están interesadas en: ¿Cómo se ha logrado el resultado?
Un trozo es un simple objeto falso. Solo se asegura de que la prueba funcione sin problemas.
Un simulacro es un trozo más inteligente. Verifica que su prueba lo pasa.
Aquí hay una descripción de cada uno seguido de una muestra del mundo real.
Dummy : solo valores falsos para satisfacer el API
.
Ejemplo : si está probando un método de una clase que requiere muchos parámetros obligatorios en un constructor que no tienen ningún efecto en su prueba, puede crear objetos ficticios con el fin de crear nuevas instancias de una clase.
Fake : crea una implementación de prueba de una clase que puede depender de alguna infraestructura externa. (Es una buena práctica que la prueba de la unidad NO interactúe realmente con la infraestructura externa).
Ejemplo : crear una implementación falsa para acceder a una base de datos, reemplazarla con una
in-memory
colección.
Stub : métodos de anulación para devolver valores codificados, también conocidos como state-based
.
Ejemplo : su clase de prueba depende de un método que
Calculate()
demore 5 minutos en completarse. En lugar de esperar 5 minutos, puede reemplazar su implementación real con código auxiliar que devuelve valores codificados; tomando solo una pequeña fracción del tiempo.
Simulacro : muy similar Stub
pero interaction-based
no basado en el estado. Esto significa que no espera Mock
devolver algún valor, sino asumir que se realiza un orden específico de llamadas a métodos.
Ejemplo: está probando una clase de registro de usuario. Después de llamar
Save
, debería llamarSendConfirmationEmail
.
Stubs
y en Mocks
realidad son subtipos de Mock
, ambos intercambian implementación real con implementación de prueba, pero por diferentes razones específicas.
En el curso codeschool.com , Rails Testing for Zombies , dan esta definición de los términos:
Talón
Para reemplazar un método con código que devuelve un resultado especificado.
Burlarse de
Un trozo con una afirmación de que se llama al método.
Entonces, como Sean Copenhaver describió en su respuesta, la diferencia es que se burla de las expectativas establecidas (es decir, hace afirmaciones sobre si se les llama o cómo se llaman).
Los trozos no fallan en las pruebas, simulacro puede.
Creo que la respuesta más simple y clara sobre esta pregunta proviene de Roy Osherove en su libro The art of Unit Testing (página 85)
La forma más fácil de saber que estamos lidiando con un trozo es notar que el trozo nunca puede fallar la prueba. Las afirmaciones que usa la prueba siempre están en contra de la clase bajo prueba.
Por otro lado, la prueba utilizará un objeto simulado para verificar si la prueba falló o no. [...]
Nuevamente, el objeto simulado es el objeto que usamos para ver si la prueba falló o no.
Eso significa que si está haciendo afirmaciones contra el falso, significa que está usando el falso como un simulacro, si está usando el falso solo para ejecutar la prueba sin afirmación, está usando el falso como un trozo.
Leyendo todas las explicaciones anteriores, déjame intentar condensar:
Un simulacro solo está probando el comportamiento, asegurándose de que se invoquen ciertos métodos. Un trozo es una versión comprobable (per se) de un objeto en particular.
¿Qué quieres decir con Apple?
Si lo compara con la depuración:
Stub es como asegurarse de que un método devuelva el valor correcto
Mock es como entrar en el método y asegurarse de que todo lo que hay dentro sea correcto antes de devolver el valor correcto.
El uso de un modelo mental realmente me ayudó a comprender esto, en lugar de todas las explicaciones y artículos, que no se "hundieron".
Imagina que tu hijo tiene una placa de vidrio sobre la mesa y comienza a jugar con ella. Ahora, tienes miedo de que se rompa. Entonces, le das un plato de plástico en su lugar. Eso sería un simulacro (mismo comportamiento, misma interfaz, implementación "más suave").
Ahora, digamos que no tiene el reemplazo de plástico, entonces explique "¡Si continúa jugando con él, se romperá!". Eso es un trozo , proporcionó un estado predefinido de antemano.
Un Dummy sería el tenedor que ni siquiera usó ... y un Spy podría ser algo así como proporcionar la misma explicación que ya usó que funcionó.
Creo que la diferencia más importante entre ellos son sus intenciones.
Voy a tratar de explicarlo en QUÉ talón vs QUÉ simulacro
Supongamos que estoy escribiendo un código de prueba para el controlador de línea de tiempo público de mi cliente de twitter mac
Aquí está el código de muestra de prueba
twitter_api.stub(:public_timeline).and_return(public_timeline_array)
client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)
controller.refresh_public_timeline
Al escribir simulacro, descubre la relación de colaboración de los objetos verificando que se cumplan las expectativas, mientras que el código auxiliar solo simula el comportamiento del objeto.
Sugiero leer este artículo si estás tratando de saber más sobre simulacros: http://jmock.org/oopsla2004.pdf
Para ser muy claro y práctico:
Stub: una clase u objeto que implementa los métodos de la clase / objeto a falsificar y devuelve siempre lo que desea.
Ejemplo en JavaScript:
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
Mock: lo mismo de stub, pero agrega algo de lógica que "verifica" cuando se llama a un método, por lo que puede estar seguro de que alguna implementación está llamando a ese método.
Como dice @mLevan, imagine como ejemplo que está probando una clase de registro de usuario. Después de llamar a Guardar, debe llamar a SendConfirmationEmail.
Un código muy estúpido Ejemplo:
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
Esta diapositiva explica muy bien las principales diferencias.
* De CSE 403 Lecture 16, Universidad de Washington (diapositiva creada por "Marty Stepp")
Me gusta la explicación de Roy Osherove [enlace de video] .
Cada clase u objeto creado es falso. Es un simulacro si verifica las llamadas en su contra. De lo contrario, es un trozo.
veamos Test Dobles:
Stub : Stub es un objeto que contiene datos predefinidos y los usa para responder llamadas durante las pruebas. Tales como : un objeto que necesita tomar algunos datos de la base de datos para responder a una llamada al método.
Simulacros : los simulacros son objetos que registran las llamadas que reciben. En la afirmación de prueba, podemos verificar en Mocks que todas las acciones esperadas se realizaron. Tales como : una funcionalidad que llama al servicio de envío de correo electrónico. para más solo verifique esto .
Un falso es un término genérico que se puede utilizar para describir un trozo o un objeto simulado (escrito a mano o de otro modo), porque ambos se parecen al objeto real.
Si un falso es un trozo o un simulacro depende de cómo se use en la prueba actual. Si se usa para verificar una interacción (afirmada en contra), es un objeto simulado. De lo contrario, es un trozo.
Fakes se asegura de que la prueba funcione sin problemas. Significa que el lector de su prueba futura comprenderá cuál será el comportamiento del objeto falso, sin necesidad de leer su código fuente (sin necesidad de depender de un recurso externo).
¿Qué significa que la prueba funcione sin problemas?
Por ejemplo en el siguiente código:
public void Analyze(string filename)
{
if(filename.Length<8)
{
try
{
errorService.LogError("long file entered named:" + filename);
}
catch (Exception e)
{
mailService.SendEMail("admin@hotmail.com", "ErrorOnWebService", "someerror");
}
}
}
Desea probar el método mailService.SendEMail () , para hacerlo necesita simular una excepción en su método de prueba, por lo que solo necesita crear una clase Fake Stub errorService para simular ese resultado, luego su código de prueba podrá probar Método mailService.SendEMail (). Como puede ver, necesita simular un resultado que provenga de otra clase External Dependency ErrorService.
Desde el papel Mock Roles, no Objects , por los desarrolladores de jMock:
Los stubs son implementaciones ficticias de código de producción que devuelven resultados fijos. Los objetos simulados actúan como trozos, pero también incluyen aserciones para instrumentar las interacciones del objeto de destino con sus vecinos.
Entonces, las principales diferencias son:
En resumen, al mismo tiempo que trata de dispersar la confusión del título del artículo de Fowler : los simulacros son trozos, pero no son solo trozos .
Estaba leyendo El arte de las pruebas unitarias , y me topé con la siguiente definición:
Un falso es un término genérico que se puede usar para describir un trozo o un objeto simulado (escrito a mano o de otro modo), porque ambos se parecen al objeto real. Si un falso es un trozo o un simulacro depende de cómo se use en la prueba actual. si se usa para verificar una interacción (afirmada contra), es un objeto simulado . De lo contrario, es un trozo .
Encontré este interesante artículo de UncleBob The Little Mocker . Explica toda la terminología de una manera muy fácil de entender, por lo que es útil para principiantes. El artículo de Martin Fowlers es una lectura difícil, especialmente para principiantes como yo.
Stub nos ayuda a ejecutar la prueba. ¿Cómo? Da valores que ayudan a ejecutar la prueba. Estos valores en sí mismos no son reales y creamos estos valores solo para ejecutar la prueba. Por ejemplo, creamos un HashMap para darnos valores que son similares a los valores en la tabla de la base de datos. Entonces, en lugar de interactuar directamente con la base de datos, interactuamos con Hashmap.
Mock es un objeto falso que ejecuta la prueba. donde ponemos afirmar
Vea a continuación el ejemplo de simulacros vs stubs usando C # y el marco Moq. Moq no tiene una palabra clave especial para Stub, pero también puede usar el objeto Mock para crear stub.
namespace UnitTestProject2
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
[TestClass]
public class UnitTest1
{
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method calls Repository GetName method "once" when Id is greater than Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Once);
}
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method doesn't call Repository GetName method when Id is Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(0);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Never);
}
/// <summary>
/// Test using Stub to Verify that GetNameWithPrefix method returns Name with a Prefix
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
{
// Arrange
var stubEntityRepository = new Mock<IEntityRepository>();
stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
.Returns("Stub");
const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
var entity = new EntityClass(stubEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
}
}
public class EntityClass
{
private IEntityRepository _entityRepository;
public EntityClass(IEntityRepository entityRepository)
{
this._entityRepository = entityRepository;
}
public string Name { get; set; }
public string GetNameWithPrefix(int id)
{
string name = string.Empty;
if (id > 0)
{
name = this._entityRepository.GetName(id);
}
return "Mr. " + name;
}
}
public interface IEntityRepository
{
string GetName(int id);
}
public class EntityRepository:IEntityRepository
{
public string GetName(int id)
{
// Code to connect to DB and get name based on Id
return "NameFromDb";
}
}
}
Punto de vista de prueba Stub y Mock:
Stub es una implementación ficticia realizada por el usuario de forma estática , es decir, en Stub escribiendo el código de implementación. Por lo tanto, no puede manejar la definición del servicio y la condición dinámica. Normalmente esto se hace en el marco JUnit sin usar el marco burlón.
Mock también es una implementación ficticia, pero su implementación se realizó de manera dinámica mediante el uso de frameworks Mocking como Mockito. Por lo tanto, podemos manejar la definición de condición y servicio de forma dinámica, es decir, se pueden crear simulacros dinámicamente a partir del código en tiempo de ejecución. Entonces, usando simulacro podemos implementar Stubs dinámicamente.
Además de respuestas útiles, una de las más puntos poderosos de usar simulacros que Subs
Si el colaborador [del cual depende el código principal] no está bajo nuestro control (p. Ej., De una biblioteca de terceros),
en este caso, el código auxiliar es más difícil de escribir que simular .
He usado ejemplos de python en mi respuesta para ilustrar las diferencias.
Stub : Stubbing es una técnica de desarrollo de software utilizada para implementar métodos de clases al principio del ciclo de vida del desarrollo. Se usan comúnmente como marcadores de posición para la implementación de una interfaz conocida, donde la interfaz se finaliza o se conoce pero la implementación aún no se conoce o finaliza. Comienzas con stubs, lo que simplemente significa que solo escribes la definición de una función y dejas el código real para más adelante. La ventaja es que no olvidará los métodos y puede seguir pensando en su diseño mientras lo ve en código. También puede hacer que su código auxiliar devuelva una respuesta estática para que otras partes de su código puedan usar la respuesta de inmediato. Los objetos de código auxiliar proporcionan una respuesta válida, pero es estática, independientemente de la entrada que ingrese, siempre obtendrá la misma respuesta:
class Foo(object):
def bar1(self):
pass
def bar2(self):
#or ...
raise NotImplementedError
def bar3(self):
#or return dummy data
return "Dummy Data"
Burlarse de objetos simulados se usan en casos de prueba simulados que validan que ciertos métodos se invocan en esos objetos. Los objetos simulados son objetos simulados que imitan el comportamiento de los objetos reales de manera controlada. Por lo general, crea un objeto simulado para probar el comportamiento de otro objeto. Los simulacros nos permiten simular recursos que no están disponibles o son demasiado difíciles de manejar para pruebas unitarias.
mymodule.py:
import os
import os.path
def rm(filename):
if os.path.isfile(filename):
os.remove(filename)
test.py:
from mymodule import rm
import mock
import unittest
class RmTestCase(unittest.TestCase):
@mock.patch('mymodule.os')
def test_rm(self, mock_os):
rm("any path")
# test that rm called os.remove with the right parameters
mock_os.remove.assert_called_with("any path")
if __name__ == '__main__':
unittest.main()
Este es un ejemplo muy básico que solo ejecuta rm y afirma el parámetro con el que fue llamado. Puede usar simulacros con objetos no solo funciones como se muestra aquí, y también puede devolver un valor para que se pueda usar un objeto simulado para reemplazar un trozo para la prueba.
Más información sobre unittest.mock , la nota en python 2.x mock no está incluida en unittest, pero es un módulo descargable que se puede descargar a través de pip (pip install mock).
También he leído "El arte de las pruebas unitarias" de Roy Osherove y creo que sería genial si se escribiera un libro similar utilizando ejemplos de Python y Python. Si alguien sabe de tal libro, por favor comparta. Salud :)
Un trozo es un objeto falso creado con fines de prueba. Un simulacro es un trozo que registra si las llamadas esperadas ocurrieron efectivamente.
Un trozo es una función vacía que se utiliza para evitar excepciones no controladas durante las pruebas:
function foo(){}
Un simulacro es una función artificial que se utiliza para evitar dependencias del sistema operativo, el entorno o el hardware durante las pruebas:
function foo(bar){ window = this; return window.toString(bar); }
En términos de afirmaciones y estado:
Referencias
muchas respuestas válidas allí arriba, pero creo que vale la pena mencionar este formulario, tío Bob: https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html
¡La mejor explicación con ejemplos!
Un simulacro es tanto un objeto técnico como funcional .
El simulacro es técnico . De hecho, es creado por una biblioteca de imitación (EasyMock, JMockit y más recientemente Mockito son conocidos por estos) gracias a la generación de código de bytes .
La implementación simulada se genera de una manera en la que podríamos instrumentarla para devolver un valor específico cuando se invoca un método, pero también algunas otras cosas, como verificar que se invocó un método simulado con algunos parámetros específicos (verificación estricta) o cualesquiera que sean los parámetros ( sin control estricto).
Instanciar un simulacro:
@Mock Foo fooMock
Grabar un comportamiento:
when(fooMock.hello()).thenReturn("hello you!");
Verificación de una invocación:
verify(fooMock).hello()
Claramente, esta no es la forma natural de instanciar / anular la clase / comportamiento de Foo. Por eso me refiero a un aspecto técnico.
Pero el simulacro también es funcional porque es una instancia de la clase que necesitamos aislar del SUT. Y con comportamientos grabados en él, podríamos usarlo en el SUT de la misma manera que lo haríamos con un trozo.
El código auxiliar es solo un objeto funcional : es una instancia de la clase que necesitamos aislar del SUT y eso es todo. Eso significa que tanto la clase de código auxiliar como todos los accesorios de comportamiento necesarios durante nuestras pruebas unitarias deben definirse explícitamente.
Por ejemplo, para stub hello()
necesitaría subclasificar la Foo
clase (o implementar su interfaz que tiene) y anular hello()
:
public class HelloStub extends Hello{
public String hello {
return "hello you!";
}
}
Si otro escenario de prueba requiere otro retorno de valor, probablemente tendremos que definir una forma genérica para establecer el retorno:
public class HelloStub extends Hello{
public HelloStub(String helloReturn){
this.helloReturn = helloReturn;
}
public String hello {
return helloReturn;
}
}
Otro escenario: si tuviera un método de efectos secundarios (sin retorno) y verificara que ese método fue invocado, probablemente debería haber agregado un booleano o un contador en la clase stub para contar cuántas veces se invocó el método.
Conclusión
El código auxiliar a menudo requiere mucha sobrecarga / código para escribir para su prueba unitaria. Qué simulación evita gracias a que proporciona funciones de grabación / verificación listas para usar.
Es por eso que hoy en día, el enfoque stub rara vez se usa en la práctica con el advenimiento de excelentes bibliotecas simuladas.
Sobre el artículo de Martin Fowler: No creo que sea un programador "simulador" mientras uso simulacros y evito trozos.
Pero uso simulacro cuando realmente es necesario (dependencias molestas) y estoy a favor de las pruebas de corte y mini-integración de prueba cuando pruebo una clase con dependencias cuya burla sería una sobrecarga.