Resolução de injeção de dependência e teste de unidade
-
03-07-2019 - |
Pergunta
Estou tentando aprender injeção de dependência e encontrei um problema ao testar o aplicativo.
Estou escrevendo um aplicativo de console e o contêiner é criado e inicializado em main (), está disponível como um get-property
dentro Program.Container
, então em qualquer lugar do meu aplicativo que eu possa ligar Program.Container.Resolve<..>()
.
Eu tenho uma aula do ServiceValidator como esta:
public class ServiceValidator
{
private readonly IConfiguration _configuration;
private readonly IService _service;
public ServiceValidator(IConfiguration configuration, IService service)
{
_configuration = configuration;
_service = service;
}
Em outra aula que eu uso
ServiceValidator serviceValidator = Program.Container.Resolve<ServiceValidator>();
serviceValidator.VerifyVersion();
É o chamado para Program.Container.Resolve
Isso me causa problemas no teste de unidade, pois não foi configurado.
Isso é uma prática ruim, para chamar a resolução no contêiner? Eu poderia criar a instância do ServiceValidator em Main()
E passe o objeto, mas isso parece estúpido, pois causaria muitos parâmetros para os objetos que acabaram de passar para o próximo método.
Então, acho que é aceitável chamar a resolução dentro de uma classe, mas o contêiner deve ser configurado para o teste de unidade. Como devo fazer isso, devo mover o contêiner para outro lugar além da classe do programa? O que você recomendaria?
Se isso importa, estou usando o Unity e o C#
Obrigado :-)
Solução
Isso é uma prática ruim, para chamar a resolução no contêiner? Eu poderia criar a instância do ServiceValidator em Main () e passar o objeto, mas isso parece estúpido, pois causaria muitos parâmetros para os objetos que acabaram de passar para o próximo método.
Quando você usa a injeção de dependência, não precisará passar muitos parâmetros para objetos. O construtor de cada objeto deve ter como parâmetros apenas as dependências que ele próprio usa diretamente - ele não saberá sobre as dependências transitivas de suas dependências diretas.
Portanto, se você possui uma classe X que exige um ServiceValidator, a classe X terá um parâmetro construtor do Type ServiceValidator. Então, se alguns Classe Y usarem classe X, então a classe Y terá um parâmetro construtor do tipo X. Observe que Y não sabe nada Sobre o ServiceValidator, para que você não precise passar pelo ServiceValidator de uma classe para outra - o único lugar onde é usado é quando a construção de X, e isso é frequentemente feito por uma estrutura DI ou em apenas um lugar em um lugar escrito à mão fábrica.
Alguns links para mais informações:
- http://martinfowler.com/articles/injection.html
- http://www.youtube.com/watch?v=rlflcwkxhj0 - Sua pergunta sobre a passagem de objetos é respondida a partir das 19:20 "Mito sobre Di"
Outras dicas
Normalmente, permito que as chamadas resolvam dependências do contêiner em lugares como o principal, embora ainda tente mantê -las no mínimo. O que eu faço então é configurar o contêiner em um método de inicialização de uma classe de teste. Eu o inicializei com implementações falsas para qualquer classe de teste que precise chamar o contêiner.
As classes de teste que não chamam nada de exigir que o contêiner sejam inicializadas podem ignorá -lo e não usar as falsificações. Eu geralmente uso zombarias nessas instâncias.
Eu também uso o Microsoft Service Locator para que a dependência que estou tomando é de algo da estrutura .NET em vez de em um contêiner específico. Isso me permite que eu use o que eu quiser até mesmo um recipiente fabricado em casa.
Você pode usar uma classe estática como inicializador para o seu contêiner. Algo como o bootstrapper.cs seria bom. Você pode fazer referência aos métodos de classe tanto em seu código quanto em testes.
Bem, o que você está fazendo tecnicamente é um local de serviço em sua classe.
Lembro -me de ler este artigo há um tempo:
http://martinfowler.com/articles/injection.html
Nas minhas aulas, nunca tento usar a resolução nelas. Eu crio os objetos através do contêiner quando preciso deles. Para testes de unidade, uso algumas classes de biblioteca e stub simuladas.
O problema está no fato de você estar tentando testar o método principal. Este método é praticamente impossível de unidade de teste.
Eu argumentaria que é melhor não testar seu método principal porque:
- A ênfase dos testes modernos de unidade é sobre design
- Você deve minimizar a dependência da configuração nos testes de unidade. A configuração pode ser testada com testes de fumaça ou integração.