Pergunta

Há algum tempo li o Simulações não são esboços artigo de Martin Fowler e devo admitir que estou com um pouco de medo de dependências externas em relação à complexidade adicional, então gostaria de perguntar:

Qual é o melhor método a ser usado durante o teste de unidade?

É melhor sempre usar uma estrutura simulada para simular automaticamente as dependências do método que está sendo testado ou você prefere usar mecanismos mais simples, como, por exemplo, stubs de teste?

Foi útil?

Solução

Como diz o mantra: 'Vá com a coisa mais simples que pode funcionar.'

  1. Se aulas falsas podem dar conta do recado, vá com elas.
  2. Se você precisar de uma interface com vários métodos para zombar, escolha uma estrutura simulada.

Evite usar simulações sempre porque eles tornam os testes frágeis.Seus testes agora têm um conhecimento complexo dos métodos chamados pela implementação, se a interface simulada ou sua implementação mudar...seus testes quebram.Isso é ruim porque você gastará mais tempo executando seus testes, em vez de apenas executar seu SUT. Os testes não devem ser inapropriadamente íntimos da implementação.
Então use seu melhor julgamento.Eu prefiro simulações quando isso me ajuda a economizar a atualização de uma classe falsa com n >> 3 métodos.

Atualizar Epílogo/Deliberação:
(Obrigado a Toran Billups pelo exemplo de um teste mockista.Veja abaixo)
Olá Doug, bem, acho que transcendemos para outra guerra santa - TDDers clássicos vs TDDers simulados.Acho que pertenço ao primeiro.

  • Se eu estiver no teste#101 Test_ExportProductList e descobrir que preciso adicionar um novo parâmetro a IProductService.GetProducts().Eu faço isso para obter este teste verde.Eu uso uma ferramenta de refatoração para atualizar todas as outras referências.Agora eu acho que todos os testes mockistas chamando esse membro explodiram.Então tenho que voltar e atualizar todos esses testes – uma perda de tempo.Por que ShouldPopulateProductsListOnViewLoadWhenPostBackIsFalse falhou?Foi porque o código está quebrado?Em vez disso, os testes estão quebrados.Eu sou a favor do uma falha de teste = 1 lugar para corrigir.A frequência de zombaria vai contra isso.Os stubs seriam melhores?Se eu tivesse um fake_class.GetProducts()..com certeza Um lugar para mudar, em vez de uma cirurgia espingarda em várias ligações do Expect.No final é uma questão de estilo..se você tivesse um método utilitário comum MockHelper.SetupExpectForGetProducts() - isso também seria suficiente.mas você verá que isso é incomum.
  • Se você colocar uma faixa branca no nome do teste, o teste será difícil de ler.Muitos códigos de encanamento para a estrutura simulada ocultam o teste real que está sendo executado.
  • requer que você aprenda esse tipo específico de estrutura de simulação

Outras dicas

Geralmente prefiro usar simulações por causa das expectativas.Quando você chama um método em um stub que retorna um valor, normalmente ele retorna apenas um valor.Mas quando você chama um método em uma simulação, ele não apenas retorna um valor, mas também impõe a expectativa de que você configurou que o método foi chamado em primeiro lugar.Em outras palavras, se você configurar uma expectativa e não chamar esse método, uma exceção será lançada.Quando você define uma expectativa, está essencialmente dizendo "se esse método não for chamado, algo deu errado". E o oposto é verdadeiro, se você chamar um método de uma simulação e não estabeleceu uma expectativa, ele lançará uma exceção, dizendo essencialmente "ei, o que você está fazendo chamando esse método quando não esperava".

Às vezes você não quer expectativas em cada método que você está chamando, então algumas estruturas de simulação permitirão simulações "parciais" que são como híbridos de simulação/stub, em que apenas as expectativas que você define são aplicadas e todas as outras chamadas de método são tratadas mais como um esboço, pois apenas retorna um valor.

Um lugar válido para usar stubs que consigo imaginar é quando você está introduzindo testes em código legado.Às vezes é mais fácil fazer um esboço criando uma subclasse da classe que você está testando do que refatorar tudo para tornar a simulação fácil ou até mesmo possível.

E para isso...

Evite usar simulações sempre porque elas tornam os testes frágeis.Seus testes agora têm um conhecimento complexo dos métodos chamados pela implementação, se a interface simulada mudar...seus testes quebram.Então use seu bom senso..<

...Eu digo que se minha interface mudar, é melhor que meus testes sejam interrompidos.Porque o objetivo dos testes de unidade é que eles testem com precisão meu código como ele existe agora.

É melhor usar uma combinação e você terá que usar seu próprio julgamento.Aqui estão as diretrizes que uso:

  • Se fazer uma chamada para código externo fizer parte do comportamento esperado (voltado para fora) do seu código, isso deverá ser testado.Use uma simulação.
  • Se a chamada for realmente um detalhe de implementação com o qual o mundo exterior não se importa, prefira os stubs.No entanto:
  • Se você está preocupado com a possibilidade de implementações posteriores do código testado contornarem acidentalmente seus stubs e deseja observar se isso acontecer, use uma simulação.Você está acoplando seu teste ao seu código, mas é para perceber que seu stub não é mais suficiente e seu teste precisa ser refeito.

O segundo tipo de zombaria é uma espécie de mal necessário.Na verdade, o que está acontecendo aqui é que, quer você use um stub ou um mock, em alguns casos você terá que acoplar ao seu código mais do que gostaria.Quando isso acontecer, é melhor usar um mock do que um stub apenas porque você saberá quando o acoplamento for quebrado e seu código não estiver mais escrito da maneira que seu teste imaginou que seria.Provavelmente é melhor deixar um comentário em seu teste ao fazer isso, para que quem o quebrar saiba que seu código não está errado, o teste está.

E, novamente, este é um cheiro de código e um último recurso.Se você achar que precisa fazer isso com frequência, tente repensar a maneira como você escreve seus testes.

Depende apenas do tipo de teste que você está fazendo.Se você estiver fazendo testes baseados em comportamento, talvez queira uma simulação dinâmica para verificar se ocorre alguma interação com sua dependência.Mas se você estiver fazendo testes baseados em estado, você pode querer um stub para verificar os valores/etc.

Por exemplo, no teste abaixo, você percebe que eu retiro a visualização para poder verificar se um valor de propriedade está definido (teste baseado em estado).Em seguida, crio uma simulação dinâmica da classe de serviço para garantir que um método específico seja chamado durante o teste (teste baseado em interação/comportamento).

<TestMethod()> _
Public Sub Should_Populate_Products_List_OnViewLoad_When_PostBack_Is_False()
    mMockery = New MockRepository()
    mView = DirectCast(mMockery.Stub(Of IProductView)(), IProductView)
    mProductService = DirectCast(mMockery.DynamicMock(Of IProductService)(), IProductService)
    mPresenter = New ProductPresenter(mView, mProductService)
    Dim ProductList As New List(Of Product)()
    ProductList.Add(New Product())
    Using mMockery.Record()
        SetupResult.For(mView.PageIsPostBack).Return(False)
        Expect.Call(mProductService.GetProducts()).Return(ProductList).Repeat.Once()
    End Using
    Using mMockery.Playback()
        mPresenter.OnViewLoad()
    End Using
    'Verify that we hit the service dependency during the method when postback is false
    Assert.AreEqual(1, mView.Products.Count)
    mMockery.VerifyAll()
End Sub

Não importa Estatista vs.Interação.Pense nas funções e relacionamentos.Se um objeto colabora com um vizinho para realizar seu trabalho, então esse relacionamento (conforme expresso em uma interface) é candidato a teste usando simulações.Se um objeto for um objeto de valor simples com um pouco de comportamento, teste-o diretamente.Não vejo sentido em escrever simulações (ou mesmo esboços) à mão.Foi assim que todos nós começamos e refatoramos isso.

Para uma discussão mais longa, considere dar uma olhada em http://www.mockobjects.com/book

Leia a discussão de Luke Kanies exatamente sobre esta questão em esta postagem do blog.Ele faz referência uma postagem de Jay Fields o que até sugere que usar [um equivalente ao stub_everything de Ruby/mocha] é preferível para tornar os testes mais robustos.Para citar as palavras finais de Fields:"O Mocha torna tão fácil definir um mock quanto definir um stub, mas isso não significa que você deva sempre preferir mocks.Na verdade, geralmente prefiro stubs e uso mocks quando necessário."

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top