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?
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:
ReturnsUsingContext
(IIRC, você pode simplesmente copiar a implementação atual e remover a linha 104)MockVirtualMethodsCommand
e substitua a chamada para ReturnsUsingContext
por uma chamada para seu novo método.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:
AutoConfiguredMoqCustomization
, que seria propagado para MockVirtualMethodsCommand
e para MockType.ReturnsUsingContext
interno. Isto parece: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 😉
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.