Cobertura de testes para construtor utilizado somente na classe de testes

Estou realizando testes onde preciso mockar um DAO e no framework que estou trabalhando não existe injeção de dependência, portanto estou passando o DAO mockado pelo construtor da classe.

Resumindo: tenho um construtor para classe de produção:

public ModeloService(Modelo modelo, Funcionario usuarioLogado) throws ApplicationException {
	this(hibernateFactory.getModeloDAO(), hibernateFactory.getFuncionarioDAO(), hibernateFactory.getModeloRascunhoDAO(), modelo, usuarioLogado);
}

e outro para classe de testes, onde é injeado o DAO mockado:

public ModeloService(ModeloDAO modeloDAO, FuncionarioDAO funcionarioDAO, ModeloRascunhoDAO modeloRascunhoDAO, Modelo modelo, Funcionario usuarioLogado){
	this.modeloDAO = modeloDAO;
	this.funcionarioDAO = funcionarioDAO;
	this.modeloRascunhoDAO = modeloRascunhoDAO;
	this.modelo = modeloDAO.obterCompleto(modelo.getCodModelo());
	this.usuarioLogado = funcionarioDAO.obter(usuarioLogado.getDoctoCPF());
}

Meu service está assim:

public class ModeloService {
	private static final DAOFactory hibernateFactory = DAOFactory.getDAOFactory(DAOFactory.HIBERNATE);

	private final ModeloDAO modeloDAO;
	private final FuncionarioDAO funcionarioDAO;
	private final ModeloRascunhoDAO modeloRascunhoDAO;
	private Modelo modelo;
	private Funcionario usuarioLogado;
}

Acontece que o construtor de produção não está sendo coberto pelos testes, pois de fato ele não é usado na classe de testes. Tem como resolver isso? Não tenho certeza se utilizar outro construtor para passar DAO no caso de teste está correto.

Não encontrei as diferenças entre os dois. Parece que você se equivocou ao copiar e colar o código dos construtores.

Corrigi o código, desculpe pelo equivoco.

Certo.
Bom, você entende que criar um construtor para o teste torna o teste inválido, não é?
Afinal, você não está testando da maneira adequada, pois o construtor que será utilizando “normalmente” foi deixado de lado.
Sugestão: mockar os DAOs e utilizar Mockito ou similar para definir o que retornar quando invocar os métodos específicos (getModeloDAO(), getFuncionarioDAO());

Concordo com você que meu teste está inválido.

Meu código de teste já possui os DAOs mockados com mockito, porém fiz essa “gambi” de passá-los pelo construtor pois foi a única forma que achei de “injetá-los” no ModeloService.

Meu problema está em como fazer os DAOS mockados serem vinculados ao ModeloService, não vejo outra forma senão no construtor. Tem ideia de como resolver?

Você não deveria ter um construtor para o teste.
Se eu fosse você, mockearia o hibernateFactory para fazer seu construtor de produção funcionar adequadamente.

1 curtida

Mas o mockito permite que você indique o que vai devolver quando invoca um método, não?
Aliás, o ideal nem seria colocar toda aquela quantidade de parâmetros num construtor, usa setters. E recupera com os getters. Pronto.

1 curtida

Exatamente isso, resolvido :slight_smile:

Exato, estava bem ruim aquela quantidade de parâmetros no construtor mesmo. Não tinha atentado pra mockar hibernateFactory, vou injetar o método getDAOFactory dele.

Obrigado pela ajuda.

Apesar de já estar resolvido, eu gostaria de fazer alguns comentários:

  • O fato do seu construtor ter muitos parâmetros estava expondo o fato da sua classe ter muitas dependências, você escondeu o problema.

  • Você pode manter na classe apenas o construtor que tinha usado no teste. No lugar do seu código de produçao onde você chama esse construtor, você também chamaria o hibernateFactory para criar seus objetos. Isso seria uma injeçao de dependência mesmo sem ter um framework para isso.

  • Você tem essa referência estática ao DAOFactory que é criada dentro da classe sem ter qualquer controle sobre a criaçao disso. Mesmo num teste, o DAOFactory tem que retornar algo válido.

  • Nao sei o resto do código, mas se você apenas usa o funcionarioDAO para carregar o usuário logado e só usa o modeloDAO para obter o modelo completo, você poderia passar diretamente no construtor um usuário logado e um modelo completo, ao invés de passar objetos parcialmente preenchidos para usar apenas o id deles (getCodModelo e getDoctoCPF)

Como em serviços pequenos eu nao uso framework de injeçao de dependências, um padrao é que uso é manter os construtores bem simples recebendo diretamente e o que precisam e ter um factory method que faz o “wiring” chamando o construtor com os parâmetros corretos. Esse factory method geralmente é simples o suficiente pra nao precisar de unit tests (mas acabam sendo cobertos em integration tests)

Inicialmente pensei em fazer isto, mas estaria correto instanciar o DAO na camada de controle? Fiz desta forma pra deixar o DAO isolado dentro da camada de serviço.

De fato é uma dependência a mais criando acoplamento, mas cai na mesma situação anterior: deveria instanciar DAO na camada de controle?

Mesmas respostas dadas antes.

Se parar pra pensar melhor seria passar os objetos de domínio prontos, mas realmente fiquei em dúvida nessa questão de onde o DAO deveria ficar, se ficar na camada controladora posso passar tudo por construtor simulando uma “injeção” e diminuindo acoplamento.

Pode dar um exemplo simples desse factory method que você usa?

Quando diz camada de controle, está falando de controllers do padrao MVC?
Mas é dentro do seus controllers que você cria seus DAOs?

Eu criaria os DAOs no mesmo lugar onde cria os controllers, e passaria pra os controllers como parâmetro no construtores também

No seu exemplo ficaria algo assim:

  //construtor
  ModeloService(final ModeloRascunhoDao modeloRascunhoDao,
                final Modelo modelo,
                final Funcionario usuarioLogado) {
    this.modeloRascunhoDao = modeloRascunhoDao;
    this.modelo = modelo;
    this.usuarioLogado = usuarioLogado;
  }

  // factory method
  public static ModeloService criar(HibernateFactory factory,
                                    String codigoModelo,
                                    String cpf) {
    return new ModeloService(hibernateFactory.getModeloRascunhoDAO(),
        hibernateFactory.getModeloDAO().obterCompleto(codigoModelo),
        hibernateFactory.getFuncionarioDAO().obter(cpf)
        );
  }
1 curtida

Correto.

Não, no código atual os DAOs são criados dentro da classe Service:

private static final DAOFactory hibernateFactory = DAOFactory.getDAOFactory(DAOFactory.HIBERNATE);

public ModeloService(Modelo modelo, Funcionario usuarioLogado) throws ApplicationException {
	this(hibernateFactory.getModeloDAO(), hibernateFactory.getFuncionarioDAO(), hibernateFactory.getModeloRascunhoDAO(), modelo, usuarioLogado);
}

Não entendi, os controllers são criados automaticamente pelo framework.

Entendi o código que passou da factory method, posso fazer desta forma também.

Minha dúvida principal: do ponto de vista do design, posso chamar minha factory de DAOs de dentro do controller? Não sei se li isso em algum lugar, mas pra mim que o controller deveria somente chamar o service, e este último trocar mensagens com classes de dados(DAOs) e classes de domínio.

E quem cria o Service?

Faz sentido como princípio, mas nao precisa ser uma lei. Se tudo que seu service fizer é delegar a chamada para um DAO, por exemplo, para que você precisaria de um service?

1 curtida

É instanciado no controller.

No caso não coloquei todo o código do Service aqui no post, ele tem mais métodos referentes ao negócio. Mas concordo com você, se só delegasse a criação do DAO não faria sentido um Service.

Estou refatorando um sistema legado que foi programado todo de forma procedural, estou estudando e testando a arquitetura de modo a tornar o design compatível com OO.

Entendi, mas acredito que isso seja equivalente a instanciar os DAOs no controller. Os controllers nao deveria estar instanciando services (ou DAOs), deveriam receber esses objetos já prontos.

Como o framework que usa instancia os controllers, talvez tenha alguma maneira de injetar dependência através deles também.

É um exercício interessante. Mas eu recomendo que foque sempre nos problemas que a arquitetura atual causa. Ficar com design OO nao deveria ser uma meta (ah menos que seja pra estudo) e sim um caminho pra alcançar um objetivo concreto (facilitar manutençao, aumentar qualidade do código, etc).

Ser procedural não é um problema. Importante é organizar, separar bem as funcionalidades, etc. Falta de organização pode acontecer em qualquer tipo de paradigma.

É um framework bastante antigo e nele não existe injeção de dependência, por isso estou tentando fazer manualmente. Talvez tenha alguma maneira de aplicar injeção de dependência nele, mas não cheguei nesta parte ainda e quero fazer manualmente pra entender exatamente o que está acontencendo.

É exatamente isso que busco, alcançar um objetivo concreto :slight_smile:

Pra mim é um problema bem grande. Você consegue testar código procedural?

Sim, basta chamar o procedimento. No seu caso o problema nao é só com a parte procedural, mas principalmente com OO mal feito. Por estar mal feito quer chamar de “procedural”.

Fui tentar implementar agora e verifiquei que o método:

DAOFactory.getDAOFactory(DAOFactory.HIBERNATE);

é estático, logo não é possível mockar. Quem implementou essa factory aqui fez “bem feito”, pra não ter como testar mesmo, hahaha.

A única forma de resolver sem ter que refatorar a factory DAO vai ser “injetar” o DAOFactory no controller como o @AbelBueno falou.