Qual é a diferença entre fingir, zombar e raspar?

706

Sei como uso esses termos, mas estou me perguntando se existem definições aceitas para falsificação , zombaria e stub para testes de unidade? Como você os define para seus testes? Descreva as situações em que você pode usar cada uma.

Aqui está como eu os uso:

Falso : uma classe que implementa uma interface, mas contém dados fixos e nenhuma lógica. Simplesmente retorna dados "bons" ou "ruins", dependendo da implementação.

Mock : uma classe que implementa uma interface e permite a capacidade de definir dinamicamente os valores para retornar / exceções a serem lançadas de métodos específicos e fornece a capacidade de verificar se métodos específicos foram chamados / não chamados.

Stub : Como uma classe simulada, exceto que ela não fornece a capacidade de verificar se os métodos foram chamados / não chamados.

Zombarias e stubs podem ser gerados manualmente ou gerados por uma estrutura de simulação. Classes falsas são geradas manualmente. Uso mocks principalmente para verificar as interações entre minha classe e as classes dependentes. Uso stubs depois de verificar as interações e testar caminhos alternativos através do meu código. Eu uso classes falsas principalmente para abstrair dependências de dados ou quando zombarias / stubs são muito entediantes para serem configurados a cada vez.

tvanfosson
fonte
6
Bem, você disse tudo, basicamente, em sua "pergunta" :) Eu acho que esses são muito bem definições desses termos aceitos
Eran Galperin
2
A definição de Fake da Wikipedia difere disso, afirmando que um Fake "é usado como uma implementação mais simples, por exemplo, usando um banco de dados na memória nos testes em vez de acessar o banco de dados real)" Veja en.wikipedia.org/wiki/Test_double
zumalifeguard 24/08
2
Aprendi muito com o recurso a seguir, com uma excelente explicação de Robert C. Martin (tio Bob): The Little Mocker no Blog do Código Limpo . Explica as diferenças entre e sutilezas de manequins, duplas de teste, stubs, espiões, zombarias (verdadeiras) e falsificações. Ele também menciona Martin Fowler e explica um pouco da história de testes de software.
Erik
testing.googleblog.com/2013/07/… (um breve resumo de uma página).
ShreevatsaR
Aqui está a minha opinião para explicar isso: Test Doubles: Fakes, Stubs and Mocks (publicação de blog com exemplos)
michal-lipski

Respostas:

548

Você pode obter algumas informações:

De Martin Fowler sobre Mock and Stub

Objetos falsos realmente têm implementações de trabalho, mas geralmente usam algum atalho que os torna inadequados para produção

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.

De xunitpattern :

Falso : Adquirimos ou construímos uma implementação muito leve da mesma funcionalidade fornecida por um componente do qual o SUT depende e instruímos o SUT a usá-lo em vez do real.

Stub : Esta implementação está configurada para responder a chamadas do SUT com os valores (ou exceções) que exercitarão o Código Não Testado (consulte Erros de Produção na página X) no SUT. Uma indicação importante para o uso de um stub de teste é ter o código não testado causado pela incapacidade de controlar as entradas indiretas do SUT

Objeto simulado que implementa a mesma interface que um objeto do qual o SUT (Sistema em teste) depende. Podemos usar um Objeto Simulado como um ponto de observação quando precisamos fazer a Verificação de Comportamento para evitar que um Requisito Não Testado (consulte Bugs de Produção na página X) seja causado por uma incapacidade de observar efeitos colaterais de métodos de chamada no SUT.

Pessoalmente

Eu tento simplificar usando: Mock e Stub. Uso Mock quando é um objeto que retorna um valor definido para a classe testada. Eu uso o Stub para imitar uma classe Interface ou Resumo a ser testada. De fato, não importa realmente como você chama, são todas as classes que não são usadas na produção e são usadas como classes de utilidade para teste.

Patrick Desjardins
fonte
9
Parece-me que as definições para Stub e Fake estão invertidas na citação xUnitPattern em comparação com a citação de Martin Fowler. Além disso, as definições de Stub e Fake de Martin Fowler são revertidas em comparação com as definições da pergunta original de tvanfosson. Na realidade, existem definições geralmente aceitas desses dois termos ou depende apenas de quem você está falando?
Simon Tewsi
3
+1 em "Eu tento simplificar usando: Mock and Stub". Essa é uma ótima ideia!
Brad Cupit #
4
Não vejo como usar apenas o Mock and Stub é uma ótima idéia. Todo teste duplo tem seus propósitos e, portanto, seus usos.
Hector Ordonez
1
Não vejo a diferença entre Fake e Mock na definição de MF.
IdontCareAboutReputationPoints
2
@MusuNaji: Na definição do MF, não há "expectativas" em relação à conversa de um Fake, a não ser que tenha uma implementação para sua interface. Por outro lado, o Mock será desafiado (esse método foi chamado?).
dbalakirev
205

Stub - um objeto que fornece respostas predefinidas para chamadas de método.

Mock - um objeto no qual você define expectativas.

Falso - um objeto com recursos limitados (para fins de teste), por exemplo, um serviço da web falso.

Teste duplo é o termo geral para stubs, zombarias e falsificações. Mas, informalmente, você costuma ouvir as pessoas simplesmente chamá-las de zombaria.

Mike
fonte
4
Alguém poderia me explicar e definir o que é uma "resposta em lata" neste contexto?
MasterMastic
14
Um valor explícito, em vez de um valor calculado.
Mike
Finalmente! Algumas definições eu posso entender! Com base nessas definições, googletest (gtest) / googlemock (gmock) permite que objetos simulados também sejam stubs, pois você pode criar EXPECT_CALL()s em um método simulado que força certas saídas com base em determinadas entradas, usando o tipo .WillOnce(Invoke(my_func_or_lambda_func))(ou com .WillRepeatedly()) sintaxe anexada a um EXPECT_CALL(). Alguns exemplos de uso Invoke()podem ser vistos em um contexto diferente na parte inferior da minha resposta longa aqui: stackoverflow.com/a/60905880/4561887 .
Gabriel Staples
A documentação do Gmock Invoke()está aqui: github.com/google/googletest/blob/master/googlemock/docs/… . De qualquer forma, a conclusão é: o Google mock (gmock) permite criar facilmente zombarias e stubs , embora a maioria das zombarias não sejam stubs.
Gabriel Staples
As zombarias são um superconjunto de stubs, mas ainda podem retornar respostas predefinidas, mas também permitem que o desenvolvedor defina expectativas. OMI certas bibliotecas lá fora, borrar as linhas de todos os manequins de teste.
Luke
94

Surpreende-me que essa pergunta já exista há tanto tempo e ninguém ainda tenha fornecido uma resposta baseada em "A arte de testar a unidade", de Roy Osherove .

Em "3.1 Introdução de stubs", define um stub como:

Um stub é um substituto controlável para uma dependência existente (ou colaborador) no sistema. Usando um stub, você pode testar seu código sem lidar diretamente com a dependência.

E define a diferença entre stubs e zombarias como:

A principal coisa a lembrar sobre zombarias versus stubs é que as zombarias são exatamente como stubs, mas você afirma contra o objeto simulado, enquanto que não afirma contra um stub.

Falso é apenas o nome usado para stubs e zombarias. Por exemplo, quando você não se importa com a distinção entre stubs e zombarias.

A maneira como o Osherove's distingue stubs e zombes significa que qualquer classe usada como falsa para teste pode ser tanto um stub quanto uma farsa. O que é para um teste específico depende inteiramente de como você faz as verificações em seu teste.

  • Quando seu teste verifica valores na classe em teste, ou na verdade em qualquer lugar que não seja o falso, o falso foi usado como um esboço. Ele apenas forneceu valores para a classe em teste usar, diretamente por meio dos valores retornados por chamadas ou indiretamente por causar efeitos colaterais (em algum estado) como resultado de chamadas por ela.
  • Quando o seu teste verifica os valores do falso, ele foi usado como uma farsa.

Exemplo de teste em que a classe FakeX é usada como um esboço:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, cut.SomeProperty);

A fakeinstância é usada como um stub porque Assertnão usa de fakejeito nenhum.

Exemplo de teste em que a classe de teste X é usada como uma farsa:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, fake.SomeProperty);

Nesse caso, o Assertvalor é verificado fake, fazendo com que seja falso.

Agora, é claro que esses exemplos são altamente planejados, mas vejo grande mérito nessa distinção. Isso o torna ciente de como você está testando suas coisas e onde estão as dependências do seu teste.

Eu concordo com Osherove que

de uma perspectiva pura de manutenção, em meus testes, usar zombarias cria mais problemas do que não usá-las. Essa tem sido a minha experiência, mas estou sempre aprendendo algo novo.

Afirmar contra o falso é algo que você realmente deseja evitar, pois torna seus testes altamente dependentes da implementação de uma classe que não é a que está sendo testada. O que significa que os testes para a classe ActualClassUnderTestpodem começar a ser interrompidos porque a implementação foi ClassUsedAsMockalterada. E isso me dá um cheiro ruim. Os testes para ActualClassUnderTest, de preferência, devem apenas interromper quando ActualClassUnderTestsão alterados.

Sei que escrever afirmações contra o falso é uma prática comum, especialmente quando você é um tipo mockista de assinante TDD. Acho que estou firmemente com Martin Fowler no campo dos classicistas (veja "Mocks are Stubs", de Martin Fowler ) e, como Osherove, evita os testes de interação (o que só pode ser feito com a afirmação contra o falso), tanto quanto possível.

Para uma leitura divertida sobre por que você deve evitar zombarias, conforme definido aqui, pesquise no Google por "classicista mockista fowler". Você encontrará uma infinidade de opiniões.

Marjan Venema
fonte
30

Conforme mencionado pela resposta mais votada, Martin Fowler discute essas distinções em Mocks Ar not Stubs e, em particular, no subtítulo The Difference Between Mocks and Stubs , portanto, leia esse artigo.

Em vez de focar em como essas coisas são diferentes, acho mais esclarecedor focar no motivo de esses serem conceitos distintos. Cada um existe para um propósito diferente.

Fakes

Um falso é uma implementação que se comporta "naturalmente", mas não é "real". Estes são conceitos difusos e, portanto, pessoas diferentes têm entendimentos diferentes sobre o que torna as coisas falsas.

Um exemplo de falsificação é um banco de dados na memória (por exemplo, usando o sqlite na :memory:loja). Você nunca usaria isso para produção (já que os dados não são persistentes), mas é perfeitamente adequado como banco de dados para uso em um ambiente de teste. Também é muito mais leve que um banco de dados "real".

Como outro exemplo, talvez você use algum tipo de armazenamento de objetos (por exemplo, Amazon S3) na produção, mas em um teste você pode simplesmente salvar objetos em arquivos no disco; então sua implementação "salvar em disco" seria falsa. (Ou você pode até fingir a operação "salvar em disco" usando um sistema de arquivos na memória.)

Como terceiro exemplo, imagine um objeto que fornece uma API de cache; um objeto que implementa a interface correta, mas que simplesmente não executa armazenamento em cache, mas sempre retorna um erro de cache, seria uma espécie de falso.

O objetivo de uma falsificação não é afetar o comportamento do sistema em teste , mas sim simplificar a implementação do teste (removendo dependências desnecessárias ou pesadas).

Stubs

Um stub é uma implementação que se comporta "de maneira não natural". É pré-configurado (geralmente pela configuração do teste) para responder a entradas específicas com saídas específicas.

O objetivo de um stub é colocar seu sistema em teste em um estado específico. Por exemplo, se você estiver gravando um teste para algum código que interaja com uma API REST, poderá remover a API REST com uma API que sempre retorne uma resposta fixa ou que responda a uma solicitação de API com um erro específico. Dessa forma, você pode escrever testes que façam afirmações sobre como o sistema reage a esses estados; por exemplo, testando a resposta que seus usuários recebem se a API retornar um erro 404.

Um stub geralmente é implementado para responder apenas às interações exatas às quais você pediu. Mas o principal recurso que faz de algo um stub é seu objetivo : um stub é sobre como configurar seu caso de teste.

Zombaria

Um mock é semelhante a um stub, mas com a verificação adicionada. O objetivo de um mock é fazer afirmações sobre como o sistema em teste interagiu com a dependência .

Por exemplo, se você estiver escrevendo um teste para um sistema que carrega arquivos em um site, você pode criar uma simulação que aceita um arquivo e que pode ser usada para afirmar que o arquivo carregado estava correto. Ou, em uma escala menor, é comum usar uma simulação de um objeto para verificar se o sistema em teste chama métodos específicos do objeto simulado.

As simulações estão vinculadas ao teste de interação , que é uma metodologia de teste específica. As pessoas que preferem testar o estado do sistema em vez de interações com o sistema usarão zombarias com moderação, se houver.

Duplas de teste

Falsificações, stubs e zombarias pertencem à categoria de duplas de teste . Um teste duplo é qualquer objeto ou sistema que você usa em um teste, em vez de outra coisa. A maioria dos testes automatizados de software envolve o uso de testes duplos de algum tipo ou de outro. Alguns outros tipos de duplas de teste incluem valores fictícios , espiões , e I / O blackholes .

Daniel Pryden
fonte
11

Para ilustrar o uso de stubs e zombarias, também gostaria de incluir um exemplo baseado em " A arte de testar unidades ", de Roy Osherove .

Imagine, temos um aplicativo LogAnalyzer que tem a única funcionalidade de imprimir logs. Ele não precisa apenas falar com um serviço da Web, mas se o serviço da Web gerar um erro, o LogAnalyzer precisará registrar o erro em uma dependência externa diferente, enviando-o por email ao administrador do serviço da Web.

Aqui está a lógica que gostaríamos de testar dentro do LogAnalyzer:

if(fileName.Length<8)
{
 try
  {
    service.LogError("Filename too short:" + fileName);
  }
 catch (Exception e)
  {
    email.SendEmail("a","subject",e.Message);
  }
}

Como você testa se o LogAnalyzer chama o serviço de email corretamente quando o serviço da Web lança uma exceção? Aqui estão as perguntas que enfrentamos:

  • Como podemos substituir o serviço da web?

  • Como podemos simular uma exceção do serviço da web para testar a chamada para o serviço de email?

  • Como saberemos que o serviço de email foi chamado corretamente ou de todo?

Podemos lidar com as duas primeiras perguntas usando um esboço para o serviço da web . Para resolver o terceiro problema, podemos usar um objeto simulado para o serviço de email .

Uma falsificação é um termo genérico que pode ser usado para descrever um esboço ou uma simulação. Em nosso teste, teremos duas falsificações. Um deles será o mock do serviço de email, que usaremos para verificar se os parâmetros corretos foram enviados ao serviço de email. O outro será um esboço que usaremos para simular uma exceção lançada do serviço da web. É um esboço porque não usaremos o serviço da web falso para verificar o resultado do teste, apenas para garantir que o teste seja executado corretamente. O serviço de email é uma farsa, porque afirmaremos que foi chamado corretamente.

[TestFixture]
public class LogAnalyzer2Tests
{
[Test]
 public void Analyze_WebServiceThrows_SendsEmail()
 {
   StubService stubService = new StubService();
   stubService.ToThrow= new Exception("fake exception");
   MockEmailService mockEmail = new MockEmailService();

   LogAnalyzer2 log = new LogAnalyzer2();
   log.Service = stubService
   log.Email=mockEmail;
   string tooShortFileName="abc.ext";
   log.Analyze(tooShortFileName);

   Assert.AreEqual("a",mockEmail.To); //MOCKING USED
   Assert.AreEqual("fake exception",mockEmail.Body); //MOCKING USED
   Assert.AreEqual("subject",mockEmail.Subject);
 }
}
nanospeck
fonte
9

o que você afirma sobre ele é chamado de objeto simulado e tudo o mais que ajudou no teste, é um esboço .

Arezoo Bagherzadi
fonte
1
enquanto outras respostas têm ótimos detalhes e são realmente boas. este torna tão claro e fácil fazer a diferença, é difícil não votar. gj!
Mario Garcia
6

É uma questão de tornar os testes expressivos. Defino as expectativas em uma simulação, se quero que o teste descreva um relacionamento entre dois objetos. Eu stub valores de retorno se estou configurando um objeto de suporte para me levar ao comportamento interessante no teste.

Steve Freeman
fonte
6

Se você estiver familiarizado com Arrange-Act-Assert, uma maneira de explicar a diferença entre stub e mock que pode ser útil para você é que os stubs pertencem à seção de organização, assim como são para organizar o estado de entrada, e as zombarias pertencem a a seção assert como são para reivindicar resultados contra.

Os manequins não fazem nada. Eles são apenas para preencher listas de parâmetros, para que você não obtenha erros indefinidos ou nulos. Eles também existem para satisfazer o verificador de tipos em idiomas estritamente digitados, para que você possa compilar e executar.

Sammi
fonte
3

Stub, Fakes e Mocks têm significados diferentes em diferentes fontes. Sugiro que você apresente os termos internos de sua equipe e concorde com o significado deles.

Eu acho que é importante distinguir entre duas abordagens: - validação de comportamento (implica substituição de comportamento) - validação de estado final (implica emulação de comportamento)

Considere o envio de e-mail em caso de erro. Ao fazer a validação comportamento - você verificar que o método Sendde IEmailSenderfoi executado uma vez. E você precisa emular o resultado do retorno desse método, retornar o ID da mensagem enviada. Então você diz: "Espero que Sendisso seja chamado. E retornarei apenas uma identificação fictícia (ou aleatória) para qualquer chamada" . Esta é a validação de comportamento: emailSender.Expect(es=>es.Send(anyThing)).Return((subject,body) => "dummyId")

Ao fazer a validação de estado, você precisará criar TestEmailSenderesses implementos IEmailSender. E implemente o Sendmétodo - salvando a entrada em alguma estrutura de dados que será usada para verificação de estado futuro, como a matriz de alguns objetos, SentEmailse depois testa você verificará se SentEmailscontém o email esperado. Esta é a validação de estado: Assert.AreEqual(1, emailSender.SentEmails.Count)

Pelas minhas leituras, entendi que a validação de comportamento geralmente se chama Mocks . E validação de estado geralmente chamada Stubs ou Fakes .

Marat Gallyamov
fonte
Definição muito bem detalhada e nítida.
shyam sundar singh tomar
2

stub e fake são objetos em que eles podem variar sua resposta com base nos parâmetros de entrada. a principal diferença entre eles é que um Fake está mais próximo de uma implementação no mundo real do que um esboço. Os stubs contêm respostas basicamente codificadas para uma solicitação esperada. Vamos ver um exemplo:

public class MyUnitTest {

 @Test
 public void testConcatenate() {
  StubDependency stubDependency = new StubDependency();
  int result = stubDependency.toNumber("one", "two");
  assertEquals("onetwo", result);
 }
}

public class StubDependency() {
 public int toNumber(string param) {
  if (param == “one”) {
   return 1;
  }
  if (param == “two”) {
   return 2;
  }
 }
}

Um mock é um passo em frente a falsificações e stubs. As zombarias fornecem a mesma funcionalidade que os stubs, mas são mais complexas. Eles podem ter regras definidas para eles que determinam em que ordem os métodos em sua API devem ser chamados. A maioria das zombarias pode rastrear quantas vezes um método foi chamado e pode reagir com base nessas informações. As zombarias geralmente conhecem o contexto de cada chamada e podem reagir de maneira diferente em diferentes situações. Por isso, as zombarias exigem algum conhecimento da classe que estão zombando. um stub geralmente não pode rastrear quantas vezes um método foi chamado ou em que ordem uma sequência de métodos foi chamada. Um mock se parece com:

public class MockADependency {

 private int ShouldCallTwice;
 private boolean ShouldCallAtEnd;
 private boolean ShouldCallFirst;

 public int StringToInteger(String s) {
  if (s == "abc") {
   return 1;
  }
  if (s == "xyz") {
   return 2;
  }
  return 0;
 }

 public void ShouldCallFirst() {
  if ((ShouldCallTwice > 0) || ShouldCallAtEnd)
   throw new AssertionException("ShouldCallFirst not first thod called");
  ShouldCallFirst = true;
 }

 public int ShouldCallTwice(string s) {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallTwice called before ShouldCallFirst");
  if (ShouldCallAtEnd)
   throw new AssertionException("ShouldCallTwice called after ShouldCallAtEnd");
  if (ShouldCallTwice >= 2)
   throw new AssertionException("ShouldCallTwice called more than twice");
  ShouldCallTwice++;
  return StringToInteger(s);
 }

 public void ShouldCallAtEnd() {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallAtEnd called before ShouldCallFirst");
  if (ShouldCallTwice != 2) throw new AssertionException("ShouldCallTwice not called twice");
  ShouldCallAtEnd = true;
 }

}
Alireza Rahmani Khalili
fonte
1

fake objecté uma implementação real da interface (protocolo) ou uma extensão usando herança ou outras abordagens que podem ser usadas para criar - é dependência. Geralmente é criado pelo desenvolvedor como uma solução mais simples para substituir alguma dependência

stub objecté um objeto nua (0, nil e métodos sem lógica) com e extra e pré-definidos (por desenvolvedor) do estado para definir valores devolvidos. Geralmente é criado por framework

mock objecté muito semelhante, stub objectmas o estado extra é alterado durante a execução do programa para verificar se algo aconteceu (o método foi chamado).

yoAlex5
fonte