Eu tenho lutado com um problema cada vez mais irritante em relação aos nossos testes de unidade que estamos implementando em minha equipe. Estamos tentando adicionar testes de unidade ao código legado que não foi bem projetado e, embora não tenhamos tido nenhuma dificuldade com a adição real dos testes, estamos começando a ter dificuldades com o desempenho dos testes.
Como exemplo do problema, digamos que você tenha um método que chama 5 outros métodos como parte de sua execução. Um teste para esse método pode ser para confirmar que um comportamento ocorre como resultado de um desses 5 outros métodos serem chamados. Portanto, como um teste de unidade deve falhar por um motivo e apenas um motivo, você deseja eliminar possíveis problemas causados por chamar esses outros 4 métodos e zombá-los. Ótimo! O teste de unidade é executado, os métodos simulados são ignorados (e seu comportamento pode ser confirmado como parte de outros testes de unidade) e a verificação funciona.
Mas há um novo problema - o teste de unidade tem um conhecimento profundo de como você confirmou que o comportamento e qualquer assinatura são alterados para qualquer um dos outros 4 métodos no futuro, ou quaisquer novos métodos que precisem ser adicionados ao 'método pai' resulta em necessidade de alterar o teste de unidade para evitar possíveis falhas.
Naturalmente, o problema poderia ser atenuado de alguma maneira, simplesmente com mais métodos realizando menos comportamentos, mas eu esperava que houvesse talvez uma solução mais elegante disponível.
Aqui está um exemplo de teste de unidade que captura o problema.
Como uma observação rápida, 'MergeTests' é uma classe de teste de unidade que herda da classe que estamos testando e substitui o comportamento conforme necessário. Esse é um 'padrão' que empregamos em nossos testes para nos permitir substituir chamadas para classes / dependências externas.
[TestMethod]
public void VerifyMergeStopsSpinner()
{
var mockViewModel = new Mock<MergeTests> { CallBase = true };
var mockMergeInfo = new MergeInfo(Mock.Of<IClaim>(), Mock.Of<IClaim>(), It.IsAny<bool>());
mockViewModel.Setup(m => m.ClaimView).Returns(Mock.Of<IClaimView>);
mockViewModel.Setup(
m =>
m.TryMergeClaims(It.IsAny<Func<bool>>(), It.IsAny<IClaim>(), It.IsAny<IClaim>(), It.IsAny<bool>(),
It.IsAny<bool>()));
mockViewModel.Setup(m => m.GetSourceClaimAndTargetClaimByMergeState(It.IsAny<MergeState>())).Returns(mockMergeInfo);
mockViewModel.Setup(m => m.SwitchToOverviewTab());
mockViewModel.Setup(m => m.IncrementSaveRequiredNotification());
mockViewModel.Setup(m => m.OnValidateAndSaveAll(It.IsAny<object>()));
mockViewModel.Setup(m => m.ProcessPendingActions(It.IsAny<string>()));
mockViewModel.Object.OnMerge(It.IsAny<MergeState>());
mockViewModel.Verify(mvm => mvm.StopSpinner(), Times.Once());
}
Como o resto de vocês lidou com isso ou não existe uma maneira "simples" de lidar com isso?
Atualização - Agradeço o feedback de todos. Infelizmente, e não é nenhuma surpresa realmente, não parece haver uma ótima solução, padrão ou prática que se possa seguir nos testes de unidade se o código que está sendo testado for ruim. Marquei a resposta que melhor capturou essa verdade simples.