Framework simulado versus frameworks MS Fakes

99

Um pouco confuso sobre as diferenças de frameworks Mock como NMock vs VS 2011 Fakes Framework. Analisando o MSDN, o que entendi é que o Fakes permite que você simule suas dependências como RhinoMock ou NMock, no entanto, a abordagem é diferente, o Fakes gera código para obter essa funcionalidade, mas o framework Mocks não. Então, meu entendimento está correto? O Fakes é apenas mais um framework mock

Nairooz NIlafdeen
fonte

Respostas:

189

Sua pergunta era sobre como o framework do MS Fakes é diferente do NMock e parece que as outras respostas resolveram parte disso, mas aqui estão mais algumas informações sobre como eles são iguais e como são diferentes. NMock também é semelhante a RhinoMocks e Moq, então estou agrupando-os com NMock.

Existem 3 diferenças principais que vejo imediatamente entre NMock / RhinoMocks / Moq e o MS Fakes Framework:

  • A estrutura do MS fakes usa código gerado, bem como Accessors em versões anteriores do Visual Studio, em vez de tipos genéricos. Quando você deseja usar a estrutura fakes para uma dependência, você adiciona o assembly que contém a dependência às referências do projeto de teste e, em seguida, clica com o botão direito nele para gerar os dublês de teste (stubs ou shims). Então, quando você está testando, na verdade está usando essas classes geradas. NMock usa genéricos para realizar a mesma coisa (ou seja IStudentRepository studentRepository = mocks.NewMock<IStudentRepository>()). Na minha opinião, a abordagem do framework MS Fakes inibe a navegação e refatoração de código de dentro dos testes, já que você está realmente trabalhando em uma classe gerada, não em sua interface real.

  • A estrutura do MS fakes fornece stubs e moles (shims), enquanto NMock, RhinoMocks e Moq fornecem stubs e mocks . Eu realmente não entendo a decisão da MS de não incluir mocks e eu, pessoalmente, não sou um fã de toupeiras pelos motivos descritos abaixo.

  • Com a estrutura MS fakes, você fornece uma implementação alternativa dos métodos que deseja criar stub. Dentro dessas implementações alternativas, você pode especificar os valores de retorno e rastrear informações sobre como ou se o método foi chamado. Com NMock, RhinoMocks e Moq, você gera um objeto simulado e usa esse objeto para especificar valores de retorno fragmentados ou para rastrear interações (se e como os métodos foram chamados). Acho a abordagem das falsificações do MS mais complexa e menos expressiva.

Para esclarecer a diferença no que as estruturas fornecem: NMock, RhinoMocks e Moq fornecem dois tipos de duplas de teste (stubs e mocks). A estrutura do fakes fornece stubs e moles (eles os chamam de shims) e, infelizmente, não inclui mocks. Para entender as diferenças e semelhanças entre NMock e MS Fakes, é útil entender quais são esses tipos diferentes de duplas de teste:

Stubs: Stubs são usados ​​quando você precisa fornecer valores para métodos ou propriedades que serão solicitados a seus dublês de teste pelo método em teste. Por exemplo, quando meu método em teste chama o método DoesStudentExist () do teste duplo IStudentRepository, eu quero que ele retorne verdadeiro.

A ideia de stubs em fakes de NMock e MS é a mesma, mas com NMock você faria algo assim:

Stub.On(mockStudentRepository).Method("DoesStudentExist").Will(Return.Value(true));

E com o MSFakes você faria algo assim:

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() // Generated by Fakes.
{
    DoesStudentExistInt32 = (studentId) => { return new Student(); }
};

Observe que no exemplo do MS Fakes você cria uma implementação inteiramente nova para o método DoesStudentExist (Observe que é chamado DoesStudentExistInt32 porque a estrutura fakes anexa os tipos de dados do parâmetro aos nomes dos métodos quando gera os objetos stub, acho que isso obscurece a clareza de Os testes). Para ser honesto, a implementação de NMock também me incomoda porque usa uma string para identificar o nome do método. (Perdoe-me se não entendi como o NMock deve ser usado.) Essa abordagem realmente inibe a refatoração e eu recomendo fortemente o RhinoMocks ou o Moq em vez do NMock por esse motivo.

Mocks: Mocks são usados ​​para verificar a interação entre o método em teste e suas dependências. Com NMock, você faz isso definindo expectativas semelhantes a esta:

Expect.Once.On(mockStudentRepository).Method("Find").With(123);

Esta é outra razão pela qual eu prefiro RhinoMocks e Moq ao invés de NMock, NMock usa o estilo de expectativa mais antigo, enquanto RhinoMocks e Moq suportam a abordagem Arrange / Act / Assert onde você especifica as interações esperadas como asserções no final do teste como este :

stubStudentRepository.AssertWasCalled( x => x.Find(123));

Novamente, observe que RhinoMocks usa um lambda em vez de uma string para identificar o método. A estrutura do ms fakes não fornece simulações. Isso significa que em suas implementações esboçadas (consulte a descrição de esboços acima), você deve definir variáveis ​​que, posteriormente, verificar se foram definidas corretamente. Seria mais ou menos assim:

bool wasFindCalled = false;

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() 
{
    DoesStudentExistInt32 = (studentId) => 
        { 
            wasFindCalled = true;
            return new Student(); 
        }
};

classUnderTest.MethodUnderTest();

Assert.IsTrue(wasFindCalled);

Acho que essa abordagem é um pouco complicada, já que você precisa rastrear a chamada no esboço e, em seguida, confirmá-la mais tarde no teste. Acho o NMock, e especialmente o RhinoMocks, exemplos mais expressivos.

Moles (Shims): Para ser franco, eu não gosto de verrugas, por causa de seu potencial para uso indevido. Uma das coisas que eu gosto tanto no teste de unidade (e TDD em particular) é que testar seu código o ajuda a entender onde você escreveu um código ruim. Isso ocorre porque testar códigos mal escritos é difícil. Isso não é verdade ao usar moles porque os moles são, na verdade, projetados para permitir que você teste dependências que não são injetadas ou para testar métodos privados. Eles funcionam de forma semelhante aos stubs, exceto que você usa um ShimsContext como este:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };
}

Minha preocupação com os shims é que as pessoas começarão a vê-los como "uma maneira mais fácil de testar a unidade", porque não o força a escrever o código da maneira que deveria. Para um artigo mais completo sobre este conceito, veja este meu post:

Para obter mais informações sobre algumas questões relacionadas aos frameworks falsos, dê uma olhada nestes posts:

Se você estiver interessado em aprender o RhinoMocks, aqui está um vídeo de treinamento Pluralsight (divulgação completa - escrevi este curso e recebo royalties pelas visualizações, mas acho que se aplica a esta discussão, então estou incluindo aqui):

Jim Cooper
fonte
14
@Jim Eu discordo que permitir que alguém "teste dependências que não são injetadas" seja uma coisa ruim. Pelo contrário, na prática, essa capacidade ajuda a evitar confundir uma base de código com muitas interfaces inúteis e a configuração / complexidade de "fiação de objeto" associada. Já vi alguns projetos (Java e .NET) que tinham centenas de interfaces Java / C # com apenas uma única classe de implementação e muitas configurações XML inúteis (para beans Spring DI ou em Web.config para MS Unity estrutura). É melhor não injetar essas dependências .
Rogério
19
@Rogerio, trabalhei em vários projetos com milhares de classes que seguem este modelo e quando a injeção de dependência é feita da maneira certa (se você está usando a configuração XML, não está fazendo certo, imho), é simples e relativamente indolor. Por outro lado, trabalhei em sistemas que não fazem isso e o acoplamento entre as classes sempre me causou uma dor significativa. O teste não é o motivo para usar injeção de dependência (embora não seja um motivo ruim). O verdadeiro motivo é o acoplamento solto. Ter interfaces que são implementadas por uma única classe nunca me causou dor.
Jim Cooper
17
@Jim, vejo a capacidade de testar projetos ruins como uma grande vantagem. A primeira coisa que você deseja fazer antes de refatorar o código legado para um design melhor é escrever testes.
Thomas Materna
4
Minha regra é que eu uso o Fakes apenas quando preciso aplicar shim a um método compartilhado / estático ou uma classe à qual não tenho acesso para implementar uma interface. Para todo o resto, uso o Moq. Fakes são mais lentos (porque os assemblies falsos precisam ser gerados) e é preciso mais esforço de codificação para corresponder à funcionalidade que você obtém com o Moq.
Nick
5
@ CarloV.Dango Em primeiro lugar, eu sei perfeitamente bem que a injeção de dependência não requer interfaces separadas; meu comentário anterior foi apenas uma alusão à tendência de criar tais interfaces ao usar DI. Em segundo lugar, "IOC" (Inversão de Controle) não tem nada a ver com DI! (O primeiro é sobre a inversão do fluxo de controle entre os métodos, enquanto o segundo é sobre a resolução de dependências para um componente / objeto.) Ao confundir IOC e DI, é você que está mostrando ignorância, não eu; e, francamente, este site não merece pessoas insultando outras pessoas, então, por favor, vamos mantê-lo civilizado.
Rogério
23

Você está correto, mas há mais nesta história. As coisas mais importantes a tirar desta resposta são:

  1. Sua arquitetura deve fazer uso adequado de stubs e injeção de dependência, em vez de depender da muleta de Fakes e mocks

  2. Falsificações e simulações são úteis para testar códigos que você não deve ou não pode alterar, como:

    • Código legado que não faz uso (ou uso eficiente) de stubs
    • APIs de terceiros
    • Recursos para os quais você não tem código-fonte

Shims (conhecido como "Moles", durante o desenvolvimento) é de fato uma estrutura de simulação que opera por meio de chamadas de desvio. Em vez de construir meticulosamente um mock (sim, até mesmo usar o Moq é relativamente doloroso!), Os shims simplesmente usam o objeto de código de produção já instalado. Os shims simplesmente redirecionam a chamada do destino de produção para o delegado de teste.

Stubs são gerados a partir de interfaces no projeto de destino. O objeto Stub é apenas isso - uma implementação da interface. O benefício de usar o tipo Stub é que você pode gerar rapidamente um stub sem bagunçar seu projeto de teste com muitos stubs de uso único, sem mencionar a perda de tempo para criá-los. Claro, você ainda deve criar stubs concretos, para uso em muitos testes.

A implementação eficiente de Fakes (tipos Shims, Mocks e Stub) demora um pouco para se acostumar, mas vale a pena o esforço. Eu pessoalmente economizei semanas de tempo de desenvolvimento, por meio do uso dos tipos Shims / Mole, Mocks e Stub. Espero que você se divirta tanto com a tecnologia quanto eu!

Mike Christian
fonte
11
Votos negados com base na falta de especificações e alguns problemas de terminologia com seus comentários sobre Fakes. Fakes é um termo geral para todos os tipos de duplas de teste e também o nome MS usado para sua biblioteca de falsificações. Em sua biblioteca, apenas os Shims são Moles, os stubs não são. Em relação à necessidade de exemplos, gostaria de ver um exemplo em sua resposta que mostra como criar um shim (ou stub) com o Fakes é mais simples do que usar o Moq. Se você fizer alterações em sua resposta para resolver isso, mudarei meu voto negativo.
Jim Cooper
1
Mas adiciona uma boa justificativa para o uso de shims (moles), ou seja, código de terceiros, APIs do sistema / SDK. Não se trata apenas de quando você está trabalhando com suas próprias soluções internas. Então, vou te dar um voto para igualar isso :-)
Tony Wall
2
@Mike e Jim .. Fiz esforço para corrigir a terminologia usada nesta resposta. Por favor, deixe-me saber se podemos melhorar. Obrigado.
Snesh
15

Pelo que entendi, a equipe do Visual Studio queria evitar a competição com as várias bibliotecas fictícias disponíveis para .NET. A EM frequentemente enfrenta decisões difíceis como essa. Eles estão condenados se não fornecem certas funcionalidades ("por que a MS não nos fornece uma biblioteca simulada; simulações são um requisito tão comum?") E condenados se o fazem ("por que a Microsoft está agindo tão agressivamente e conduzindo seus apoiadores naturais fora do mercado? ") Muitas vezes, mas nem sempre, eles decidem deixar de simplesmente fornecer suas próprias alternativas às tecnologias disponíveis e bem recebidas. Esse parece ser o caso aqui.

O recurso de correção do Fakes é muito, muito útil. Claro, existem perigos. É preciso alguma disciplina para garantir que você só use isso quando necessário. No entanto, preenche uma grande lacuna. Minha principal reclamação é que ele só é entregue com a edição Ultimate do VS 2012 e, portanto, só estará disponível para uma subseção da comunidade de desenvolvimento .NET. Que pena.

Charles Young
fonte
2
A estrutura fakes também está disponível na edição Premium.
AlwaysAProgrammer
13

Fakes inclui dois tipos diferentes de objetos "falsos". O primeiro, chamado de "stub", é essencialmente um fictício gerado automaticamente cujo comportamento padrão pode (e normalmente seria) sobrescrito para torná-lo um mock mais interessante. No entanto, faltam alguns dos recursos que a maioria das estruturas de simulação disponíveis atualmente oferece. Por exemplo, se você deseja verificar se um método em uma instância de stub foi invocado, você mesmo precisa adicionar a lógica para isso. Basicamente, se você está criando seus próprios mocks manualmente agora, os stubs provavelmente pareceriam uma melhoria. No entanto, se você já estiver usando uma estrutura de mocking mais completa, pode sentir que faltam algumas peças importantes nos stubs do Fakes.

A outra categoria de objeto oferecida pelos Fakes, chamada de "shim", expõe um mecanismo para substituir o comportamento de dependências que não foram (ou não podem ser) desacopladas adequadamente para substituição padrão por meio de simulações. AFAIK, TypeMock é o único dos principais frameworks de mocking que atualmente oferece esse tipo de funcionalidade.

A propósito, se você já experimentou o Moles antes, o Fakes é essencialmente a mesma coisa, finalmente saindo do Microsoft Research e se tornando um produto real.

Nicole Calinoiu
fonte
1
Atualização: Moles agora foi integrado ao MS Fakes. "O Fakes Framework no Visual Studio 2012 é a próxima geração de Moles e Stubs. Fakes é diferente de Moles, entretanto, mover de Moles para Fakes exigirá algumas modificações em seu código. O framework Moles não terá suporte no Visual Studio 2012 . " Fonte: research.microsoft.com/en-us/projects/moles
Sire
1

Em relação aos objetos Fake (Shim + Stub), foi bem definido acima, embora eu ache que o último parágrafo do último comentário resume toda a situação muito bem.

Embora muitas pessoas argumentem que objetos Fake (Shim + Stub) são bons ativos em alguns casos de teste de unidade, a desvantagem é que, não importa se você está usando o Visual Studio 2012 ou Visual Studio 2013, essas opções estão SOMENTE disponíveis com versões Premium ou Ultimate. IOW, isso significa que você NÃO DEVE executar QUALQUER um desses Fakes (Shim + Stub) em qualquer versão Pro.

Você provavelmente pode ver a opção de menu Fakes (Shim + Stub) nas versões Pro, mas cuidado, há algumas chances muito fortes de que você acabe sem absolutamente nada ... Não gerará nenhum erro de compilação informando que algo importante falta, as opções simplesmente não estão lá, então não perca seu tempo ...

É um fator importante a se considerar em uma equipe de desenvolvimento, especialmente se uma é a única usando a versão Ultimate enquanto todos os outros usam a versão Pro ... O Moq por outro lado pode ser facilmente instalado através do Nuget, não importa qual versão do Visual Studio você use. Não tive nenhum problema em usar o Moq, o segredo de qualquer ferramenta é saber para que serve e como usá-los corretamente;)

SirXamelot
fonte
1
Formate sua pergunta em parágrafos menores para que seja mais fácil de ler.
@SirXamelot, não refatorando o código, você não pode alterar a saída de chamadas estáticas fornecidas .net, por exemplo, DateTime.Now ou Guid.NewGuid
zaitsman