Autofixture: Opcionalmente, desative o armazenamento em cache para AutoMoq?

Criado em 20 mai. 2016  ·  14Comentários  ·  Fonte: AutoFixture/AutoFixture

Ao usar um AutoConfiguredMoqCustomization, o valor retornado pelo fixture usado é armazenado em cache, e o mesmo valor será retornado em cada invocação (não tenho certeza se isso é feito em MockType.ReturnsUsingContext ou em outro lugar, mas vejo um comentário sobre isso na linha 104 lá).

A única maneira que encontrei de sobrescrevê-lo é passar uma instância Frozen do mock em questão para o método de teste e sobrescrever a configuração com uma função que invoca o fixture:

someMock.Setup(it => it.SomeMethodReturningAString()).Returns(() => fixture.Create<string>())

Existe alguma maneira mais genérica / inteligente de configurar os simulados gerados automaticamente para invocar o aparelho cada vez que um valor é necessário do AutoFixture, diferente de configurar manualmente cada invocação?

enhancement good first issue

Comentários muito úteis

Admito que pensei que as duplas de teste foram configuradas para chamar de volta para a AutoFixtura a fim de obter um valor de retorno. O AutoFixture já tem um mecanismo de gerenciamento vitalício (por meio de seus recursos Freeze), então acho um pouco surpreendente que AutoConfiguredMoqCustomization implemente seu próprio gerenciador vitalício e, portanto, substitui a superfície de controle que o próprio AutoFixture fornece.

Todos 14 comentários

A ideia por trás do armazenamento em cache do valor do resultado era tornar todos os métodos _puros_ por padrão, o que é considerado uma boa prática.

Se o sistema em teste espera uma função pura, mas recebe uma função impura, o teste provavelmente resultará em um falso positivo. Por exemplo

// This method will return the expected result *if* `GetInt` is pure.
int Double(IDependency dep) {
    return dep.GetInt() + dep.GetInt();
}

Assert.Equal(dep.GetInt * 2, Double(dep));

Por outro lado, se o sistema sob teste não assume pureza, atribuir a ele uma função pura ou impura não deve fazer diferença.

Portanto, o padrão para funções puras fez sentido para mim quando implementei esse recurso.

Existe alguma maneira mais genérica / inteligente de configurar os simulados gerados automaticamente para invocar o aparelho cada vez que um valor é necessário do AutoFixture, diferente de configurar manualmente cada invocação?

Se você precisar fazer isso para o método _um_, mas em vários testes, eu colocaria em um IConfiguration reutilizável.

Se você quiser fazer isso para todos os métodos, em vários testes, precisará de um pouco mais de trabalho:

  • Defina uma nova implementação de ReturnsUsingContext (IIRC, você pode simplesmente copiar a implementação atual e remover a linha 104)
  • Redefina MockVirtualMethodsCommand e substitua a chamada para ReturnsUsingContext por uma chamada para seu novo método.
  • Redefina AutoConfiguredMoqCustomization e substitua a instanciação de MockVirtualMethodsCommand por uma instanciação de sua nova classe.

Também poderíamos pensar em uma maneira de tornar esse comportamento configurável, mas não tenho certeza se a demanda é alta o suficiente para justificar isso. Essa mudança não deve ser considerada levianamente, na minha opinião - a personalização refinada pode levar a uma complexidade acidental. @ploeh , o que você

Admito que pensei que as duplas de teste foram configuradas para chamar de volta para a AutoFixtura a fim de obter um valor de retorno. O AutoFixture já tem um mecanismo de gerenciamento vitalício (por meio de seus recursos Freeze), então acho um pouco surpreendente que AutoConfiguredMoqCustomization implemente seu próprio gerenciador vitalício e, portanto, substitui a superfície de controle que o próprio AutoFixture fornece.

Esse é um ponto muito bom, nunca pensei sobre Freeze dessa forma. Mudar o comportamento da customização agora seria uma mudança revolucionária, eu acredito ... você acha que deveria ser mudado no AutoFixture v4?

É um comportamento que documentamos ou de alguma forma 'prometemos' nos testes?

Surpreendentemente, não. Eu tinha certeza de que havia coberto isso, mas parece que não. Pelo menos não consegui encontrar nenhum teste que abranja isso, e remover essa linha não fez com que nenhum teste falhasse. No entanto, IMO, ainda é um comportamento observável e é provável que haja um código que depende disso ...

Ponto justo. Poderia ser um valor de configuração que definimos para um valor agora e, em seguida, trocamos quando fazemos a transição para a AutoFixture 4?

Concordou. Nesse contexto, o que exatamente você quer dizer com "valor de configuração"? Levantá-lo para uma const dentro da classe MockType seria suficiente? Por exemplo, private const bool cacheReturnValues = true e então if(cacheReturnValues) /**/

Foi um comentário meio descartável, admito, então não sei se seria praticamente possível sem muitas mudanças. O que eu quis dizer, porém, foi o seguinte:

@andreasnilsen gostaria de mudar o comportamento agora, mas estamos preocupados que seria uma mudança significativa. Se tornarmos o comportamento configurável, então daremos ao @andreasnilsen uma maneira de mudar o comportamento agora, enquanto não quebramos nenhum outro cliente.

Quando introduzimos o AutoFixture 4, mudamos a configuração padrão, para que o comportamento padrão seja que os valores de retorno não sejam armazenados em cache. Ou talvez simplesmente removamos essa opção ...

Algo como um booleano cacheReturnValues pode ser uma maneira de fazer isso, mas teríamos que permitir que os clientes alterem o valor, portanto, não pode ser private .

(Além disso, a menos que estejamos absolutamente certos de que haverá exatamente dois valores, devemos considerar enum vez de bool .)

Ah, primeiro pensei que você quisesse dizer alguma sinalização interna que fosse fácil de detectar e alterar na v4.

As duas primeiras abordagens que vêm à mente são:

  • Basta adicionar um parâmetro booleano (opcional, para compatibilidade com versões anteriores) ao construtor de AutoConfiguredMoqCustomization , que seria propagado para MockVirtualMethodsCommand e para MockType.ReturnsUsingContext interno. Isto parece:

    1. um pouco complicado e

    2. muito _ad hoc_. Se quiséssemos adicionar mais parâmetros de configuração à personalização, o construtor se tornaria muito complexo e difícil de usar / manter.

  • Use o padrão de objeto de parâmetro para resolver o problema (ii) acima.

Tenho medo de tomar decisões de design das quais podemos nos arrepender e ficar paralisados ​​mais tarde, portanto, sua expertise / experiência em manter este projeto seria muito apreciada.

Eu estava pensando em algo como uma estratégia ...

Ah sim, claro! Acho que estava obcecado por um "valor de configuração" e esqueci de dar um passo para trás.

Lamento pelo atraso na resposta, estou atolado - vou dar uma olhada nisso em breve e voltar com uma proposta de design.

Recentemente, realizamos a refatoração de customização para usar propriedades para o ajuste de customização. Potencialmente, esse recurso poderia ser controlado da seguinte forma (encontre o melhor nome de configuração):
c# new AutoMoqCustomization { CacheCallResults = false }

Nos bastidores, poderíamos implementar isso por meio de uma estratégia diferente como a que foi sugerida acima.

Marcação com um salto na tag, pois deve ser relativamente simples de implementar.

@zvirja você poderia me ajudar um pouco e eu posso trabalhar nisso. Só não sei por onde começar :)

@micheleissa Obrigado pelo seu interesse! Eu sugeriria apenas ler o código-fonte AutoMoq e depurá-lo para entender a mecânica interna. É muito pequeno, então não deve demorar muito ...

Se você tiver perguntas mais específicas, pergunte a 😉

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

zvirja picture zvirja  ·  3Comentários

mjfreelancing picture mjfreelancing  ·  4Comentários

zvirja picture zvirja  ·  4Comentários

DeafLight picture DeafLight  ·  5Comentários

tiesmaster picture tiesmaster  ·  7Comentários