Eu li vários artigos sobre zombaria ou stubbing em testes, incluindo Mocks Arn't Stubs , de Martin Fowler , mas ainda não entendo a diferença.
Eu li vários artigos sobre zombaria ou stubbing em testes, incluindo Mocks Arn't Stubs , de Martin Fowler , mas ainda não entendo a diferença.
Respostas:
Stub
Acredito que a maior distinção é que um esboço que você já escreveu com comportamento predeterminado. Portanto, você teria uma classe que implementa a dependência (classe abstrata ou interface mais provável) que você está falsificando para fins de teste e os métodos seriam eliminados com respostas definidas. Eles não fariam nada sofisticado e você já teria escrito o código stubbed fora do teste.
Zombar
Um mock é algo que, como parte de seu teste, você precisa configurar com suas expectativas. Uma simulação não é configurada de maneira predeterminada, portanto você tem um código que faz isso em seu teste. De certa forma, as zombarias são determinadas em tempo de execução, pois o código que define as expectativas precisa ser executado antes que elas façam alguma coisa.
Diferença entre zombarias e stubs
Testes escritos com zombarias geralmente seguem um initialize -> set expectations -> exercise -> verify
padrão para testes. Enquanto o esboço pré-escrito seguiria um initialize -> exercise -> verify
.
Semelhança entre zombarias e stubs
O objetivo de ambos é eliminar o teste de todas as dependências de uma classe ou função, para que seus testes sejam mais focados e mais simples no que eles estão tentando provar.
Existem várias definições de objetos que não são reais. O termo geral é teste duplo . Este termo abrange: manequim , falso , esboço , simulação .
De acordo com o artigo de Martin Fowler :
- Objetos fictícios são passados, mas nunca são realmente usados. Geralmente eles são usados apenas para preencher listas de parâmetros.
- Objetos falsos realmente têm implementações de trabalho, mas geralmente usam algum atalho que os torna inadequados para produção (um banco de dados na memória é um bom exemplo).
- Os stubs fornecem respostas prontas para as chamadas feitas durante o teste, geralmente não respondendo a nada fora do que está programado para o teste. Os stubs também podem registrar informações sobre chamadas, como um stub de gateway de e-mail que lembra as mensagens "enviadas" ou talvez apenas quantas mensagens foram "enviadas".
- Zombamos é o que estamos falando aqui: objetos pré-programados com expectativas que formam uma especificação das chamadas que eles devem receber.
Mocks vs Stubs = teste comportamental x teste estadual
De acordo com o princípio do teste, apenas uma coisa por teste , pode haver vários stubs em um teste, mas geralmente há apenas uma simulação.
Teste o ciclo de vida com stubs:
Teste o ciclo de vida com zombarias:
Os testes de zombaria e stubs respondem à pergunta: Qual é o resultado?
Testes com zombarias também estão interessados em: Como o resultado foi alcançado?
Um esboço é um objeto falso simples. Apenas garante que o teste seja executado sem problemas.
Um mock é um esboço mais inteligente. Você verifica se o seu teste passa por ele.
Aqui está uma descrição de cada um seguido por uma amostra do mundo real.
Manequim - apenas valores falsos para satisfazer o API
.
Exemplo : se você estiver testando um método de uma classe que requer muitos parâmetros obrigatórios em um construtor que não afetam seu teste, poderá criar objetos fictícios com o objetivo de criar novas instâncias de uma classe.
Falso - crie uma implementação de teste de uma classe que possa depender de alguma infraestrutura externa. (É uma boa prática que seu teste de unidade NÃO realmente interaja com a infraestrutura externa.)
Exemplo : Crie uma implementação falsa para acessar um banco de dados, substitua-o por
in-memory
coleção.
Stub - substitui métodos para retornar valores codificados, também chamados de state-based
.
Exemplo : Sua classe de teste depende de um método que
Calculate()
leva 5 minutos para ser concluído. Em vez de aguardar 5 minutos, você pode substituir sua implementação real por stub que retorna valores codificados; levando apenas uma pequena fração do tempo.
Mock - muito parecido, Stub
mas interaction-based
não baseado no estado. Isso significa que você não espera Mock
que retorne algum valor, mas suponha que uma ordem específica de chamadas de método seja feita.
Exemplo: você está testando uma classe de registro de usuário. Depois de ligar
Save
, deve ligarSendConfirmationEmail
.
Stubs
e Mocks
são, na verdade, subtipos de Mock
, ambos trocam implementação real com implementação de teste, mas por diferentes motivos específicos.
No curso codeschool.com , Rails Testing for Zombies , eles fornecem esta definição dos termos:
Stub
Para substituir um método por código que retorna um resultado especificado.
Zombar
Um stub com uma asserção de que o método é chamado.
Então, como Sean Copenhaver descreveu em sua resposta, a diferença é que a zombaria estabelece expectativas (ou seja, faz afirmações sobre se ou como elas são chamadas).
Os stubs não são reprovados em seus testes, podem sim.
Acho que a resposta mais simples e clara sobre essa questão é dada por Roy Osherove em seu livro A arte do teste de unidade (página 85)
A maneira mais fácil de dizer que estamos lidando com um esboço é perceber que o esboço nunca pode falhar no teste. As afirmações que o teste usa são sempre contra a classe em teste.
Por outro lado, o teste usará um objeto simulado para verificar se o teste falhou ou não. [...]
Novamente, o objeto simulado é o objeto que usamos para verificar se o teste falhou ou não.
Isso significa que, se você está fazendo afirmações contra o falso, significa que você está usando o falso como uma farsa; se você está usando o falso apenas para executar o teste sem afirmação, está usando o falso como um esboço.
Lendo todas as explicações acima, deixe-me tentar condensar:
Um mock está apenas testando o comportamento, certificando-se de que certos métodos sejam chamados. Um Stub é uma versão testável (por si só) de um objeto específico.
O que você quer dizer com Apple?
Se você compará-lo à depuração:
O stub é como garantir que um método retorne o valor correto
Mock é como entrar no método e garantir que tudo esteja correto antes de retornar o valor correto.
O uso de um modelo mental realmente me ajudou a entender isso, e não todas as explicações e artigos, que não "afundaram".
Imagine que seu filho tem um prato de vidro na mesa e ele começa a brincar com ele. Agora, você tem medo que isso se quebre. Então, você dá a ele um prato de plástico. Isso seria uma simulação (mesmo comportamento, mesma interface, implementação "mais suave").
Agora, diga que você não tem o substituto de plástico e explique "Se continuar jogando, ele quebrará!". Esse é um esboço , você forneceu um estado predefinido com antecedência.
Um manequim seria o garfo que ele nem usava ... e um Spy poderia ser algo como fornecer a mesma explicação que você já usou e que funcionou.
Eu acho que a diferença mais importante entre eles são suas intenções.
Deixe-me tentar explicá-lo no porquê stub vs. WHY mock
Suponha que eu esteja escrevendo um código de teste para o controlador de linha do tempo público do meu cliente do twitter do mac
Aqui está o código de amostra de teste
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
Ao escrever simulação, você descobre o relacionamento de colaboração dos objetos, verificando se as expectativas são atendidas, enquanto o stub simula apenas o comportamento do objeto.
Sugiro ler este artigo se você estiver tentando saber mais sobre zombarias: http://jmock.org/oopsla2004.pdf
Para ser muito claro e prático:
Stub: Uma classe ou objeto que implementa os métodos da classe / objeto a ser falsificado e retorna sempre o que você deseja.
Exemplo em JavaScript:
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
Mock: O mesmo do stub, mas adiciona alguma lógica que "verifica" quando um método é chamado, assim você pode ter certeza de que alguma implementação está chamando esse método.
Como o @mLevan diz, imagine como exemplo que você está testando uma classe de registro de usuário. Depois de chamar Salvar, ele deve chamar SendConfirmationEmail.
Um código muito estúpido Exemplo:
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
Este slide explica as principais diferenças muito boas.
* Da palestra CSE 403, 16, Universidade de Washington (slide criado por "Marty Stepp")
Gosto da explicação de Roy Osherove [link do vídeo] .
Toda classe ou objeto criado é falso. É uma zombaria se você verificar chamadas contra ela. Caso contrário, é um esboço.
vamos ver Duplas de teste:
Stub : Stub é um objeto que contém dados predefinidos e os utiliza para atender chamadas durante os testes. Como : um objeto que precisa capturar alguns dados do banco de dados para responder a uma chamada de método.
Zombarias : zombarias são objetos que registram as chamadas que recebem. Na asserção de teste, podemos verificar no Mocks que todas as ações esperadas foram executadas. Como : uma funcionalidade que chama o serviço de envio de email. para mais, basta verificar isso .
Uma farsa é um termo genérico que pode ser usado para descrever um esboço ou um objeto simulado (manuscrito ou não), porque ambos se parecem com o objeto real.
Se uma farsa é um esboço ou uma farsa depende de como é usada no teste atual. Se for usado para verificar uma interação (afirmada), é um objeto simulado. Caso contrário, é um esboço.
O Fakes garante que o teste seja executado sem problemas. Isso significa que o leitor do seu teste futuro entenderá qual será o comportamento do objeto falso, sem precisar ler seu código-fonte (sem precisar depender de recursos externos).
O que significa o teste executado sem problemas?
Exemplo no código abaixo:
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");
}
}
}
Você deseja testar o método mailService.SendEMail () , para fazer isso, você precisa simular uma exceção no método de teste, portanto, basta criar uma classe errorService do Fake Stub para simular esse resultado, para que seu código de teste seja capaz de testar método mailService.SendEMail (). Como você vê, você precisa simular um resultado que é de outra classe External Dependency ErrorService.
Desde o artigo Mock Roles, e não Objects , pelos desenvolvedores do jMock:
Os stubs são implementações fictícias do código de produção que retornam resultados fixos. Objetos simulados agem como stubs, mas também incluem asserções para instrumentar as interações do objeto de destino com seus vizinhos.
Portanto, as principais diferenças são:
Em resumo, ao mesmo tempo em que tenta dispersar a confusão do título do artigo de Fowler : zombarias são stubs, mas não são apenas stubs .
Eu estava lendo A arte do teste de unidade e me deparei com a seguinte definição:
Um falso é um termo genérico que pode ser usado para descrever um esboço ou um objeto falso (manuscrito ou não), porque ambos se parecem com o objeto real. Se uma farsa é um esboço ou uma farsa depende de como é usada no teste atual. se for usado para verificar uma interação (afirmada), é um objeto simulado . Caso contrário, é um esboço .
Me deparei com este artigo interessante do UncleBob The Little Mocker . Ele explica toda a terminologia de uma maneira muito fácil de entender, por isso é útil para iniciantes. O artigo de Martin Fowlers é uma leitura difícil, especialmente para iniciantes como eu.
O Stub nos ajuda a executar o teste. Quão? Fornece valores que ajudam a executar o teste. Esses valores não são reais e criamos esses valores apenas para executar o teste. Por exemplo, criamos um HashMap para fornecer valores semelhantes aos valores na tabela do banco de dados. Então, em vez de interagir diretamente com o banco de dados, interagimos com o Hashmap.
Mock é um objeto falso que executa o teste. onde colocamos afirmar.
Veja abaixo o exemplo de zombarias versus stubs usando a estrutura C # e Moq. Moq não tem uma palavra-chave especial para Stub, mas você pode usar o objeto Mock para criar stubs também.
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";
}
}
}
Ponto de vista de teste de Stub e Mock:
O Stub é uma implementação fictícia feita pelo usuário de maneira estática , ou seja, no Stub escrevendo o código de implementação. Portanto, ele não pode lidar com a definição de serviço e a condição dinâmica. Normalmente, isso é feito na estrutura JUnit sem usar a estrutura de simulação.
Mock também é uma implementação fictícia, mas sua implementação foi feita de maneira dinâmica usando estruturas de Mocking como o Mockito. Assim, podemos lidar com a definição de condição e serviço de maneira dinâmica, ou seja, simulações podem ser criadas dinamicamente a partir do código em tempo de execução. Portanto, usando o mock, podemos implementar Stubs dinamicamente.
Além de respostas úteis, um dos pontos mais poderosos do uso de Mocks do que Subs
Se o colaborador [do qual o código principal depende] não estiver sob nosso controle (por exemplo, de uma biblioteca de terceiros),
nesse caso, o stub é mais difícil de escrever do que simulado .
Eu usei exemplos de python na minha resposta para ilustrar as diferenças.
Stub - Stubbing é uma técnica de desenvolvimento de software usada para implementar métodos de classes no início do ciclo de vida do desenvolvimento. Eles são usados normalmente como espaços reservados para a implementação de uma interface conhecida, onde a interface é finalizada ou conhecida, mas a implementação ainda não é conhecida ou finalizada. Você começa com stubs, o que significa simplesmente que você apenas escreve a definição de uma função e deixa o código real para mais tarde. A vantagem é que você não esquecerá os métodos e poderá continuar pensando no seu design enquanto o vê no código. Você também pode fazer com que seu stub retorne uma resposta estática para que a resposta possa ser usada por outras partes do seu código imediatamente. Os objetos Stub fornecem uma resposta válida, mas é estática, não importa qual entrada você passe, você sempre terá a mesma resposta:
class Foo(object):
def bar1(self):
pass
def bar2(self):
#or ...
raise NotImplementedError
def bar3(self):
#or return dummy data
return "Dummy Data"
Objetos simulados são usados em casos de teste simulados e validam que certos métodos são chamados nesses objetos. Objetos simulados são objetos simulados que imitam o comportamento de objetos reais de maneira controlada. Você normalmente cria um objeto simulado para testar o comportamento de outro objeto. As zombarias permitem simular recursos indisponíveis ou muito pesados para testes de unidade.
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 é um exemplo muito básico que apenas executa rm e afirma o parâmetro com o qual foi chamado. Você pode usar simulação com objetos, não apenas funções, como mostrado aqui, e também pode retornar um valor para que um objeto falso possa ser usado para substituir um stub para teste.
Mais sobre o unittest.mock , observe que o python 2.x mock não está incluído no unittest, mas é um módulo para download que pode ser baixado via pip (pip install mock).
Também li "The Art of Unit Testing", de Roy Osherove, e acho que seria ótimo se um livro semelhante fosse escrito usando exemplos de Python e Python. Se alguém souber desse livro, compartilhe. Felicidades :)
Um stub é um objeto falso criado para fins de teste. Um mock é um esboço que registra se as chamadas esperadas ocorreram efetivamente.
Um stub é uma função vazia que é usada para evitar exceções não tratadas durante os testes:
function foo(){}
Uma simulação é uma função artificial usada para evitar dependências de SO, ambiente ou hardware durante os testes:
function foo(bar){ window = this; return window.toString(bar); }
Em termos de afirmações e estado:
Referências
muitas respostas válidas por lá, mas acho que vale a pena mencionar este formulário tio bob: https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html
a melhor explicação de todos os tempos!
Um mock é um objeto técnico e funcional .
A zombaria é técnica . É de fato criado por uma biblioteca de simulação (EasyMock, JMockit e mais recentemente Mockito são conhecidos por isso) graças à geração de código de bytes .
A implementação simulada é gerada de uma maneira em que poderíamos instrumentá- la para retornar um valor específico quando um método é chamado, mas também algumas outras coisas, como verificar se um método simulado foi chamado com alguns parâmetros específicos (verificação estrita) ou quaisquer que sejam os parâmetros ( nenhuma verificação rigorosa).
Instanciando uma simulação:
@Mock Foo fooMock
Gravando um comportamento:
when(fooMock.hello()).thenReturn("hello you!");
Verificando uma chamada:
verify(fooMock).hello()
Claramente, essa não é a maneira natural de instanciar / substituir a classe / comportamento Foo. Por isso me refiro a um aspecto técnico.
Mas o mock também é funcional porque é uma instância da classe que precisamos isolar do SUT. E com comportamentos registrados, poderíamos usá-lo no SUT da mesma maneira que faria com um esboço.
O stub é apenas um objeto funcional : é uma instância da classe que precisamos isolar do SUT e isso é tudo. Isso significa que a classe de stub e todos os acessórios de comportamento necessários durante nossos testes de unidade devem ser definidos explicitamente.
Por exemplo, o stub hello()
precisaria subclassificar a Foo
classe (ou implementar sua interface) e substituir hello()
:
public class HelloStub extends Hello{
public String hello {
return "hello you!";
}
}
Se outro cenário de teste exigir outro retorno de valor, provavelmente precisaremos definir uma maneira genérica de definir o retorno:
public class HelloStub extends Hello{
public HelloStub(String helloReturn){
this.helloReturn = helloReturn;
}
public String hello {
return helloReturn;
}
}
Outro cenário: se eu tivesse um método de efeito colateral (sem retorno) e verificasse se esse método foi chamado, provavelmente deveria ter adicionado um booleano ou um contador na classe de stub para contar quantas vezes o método foi chamado.
Conclusão
Geralmente, o stub exige muita sobrecarga / código para escrever no seu teste de unidade. Que simulação evita graças ao fornecimento de recursos de gravação / verificação prontos para uso.
É por isso que hoje em dia, a abordagem de stub raramente é usada na prática com o advento de excelentes bibliotecas simuladas.
Sobre o artigo de Martin Fowler: Não acho que seja um programador "zombeteiro" enquanto uso zombarias e evito stubs.
Mas eu uso o mock quando é realmente necessário (dependências irritantes) e sou a favor dos testes de fatia e miniintegração quando testo uma classe com dependências cujo mock seria uma sobrecarga.