Testes de classes que utilizam injeção de dependência (CDI)

206 views
Skip to first unread message

Henrique Meira

unread,
Mar 6, 2014, 2:54:08 PM3/6/14
to tdd-no-m...@googlegroups.com
Como eu posso testar uma classe que recebe um objeto via injeção?

Por exemplo, quero criar um teste para uma classe conforme segue:

public class System {
@Inject
private SqlSession session;
public List<HashMap<String, Object>> getMenuItems() {
return session.selectList(MapperResources.GET_MENU_ITEMS);
}

}


O primeiro teste, bem simples seria apenas para saber se o retorno não é nulo:

@Test
public void testGetMenuItems() {
SystemModel systemModel = new SystemModel();
assertNotNull(systemModel.getMenuItems());
}


O problema que não consegui resolver é: como injetar o SqlSession?

Grato.

Alexandre Gama

unread,
Mar 6, 2014, 3:31:42 PM3/6/14
to tdd-no-m...@googlegroups.com
Opa Henrique!

No meu caso eu prefiro fazer a injeção via construtor, assim consigo injetar tranquilamente os Mocks. No seu caso você teria que criar
um setter só para o seu teste e acho bem feio.

Faz sentido?

Abs!




--
Você recebeu essa mensagem porque está inscrito no grupo quot;TDD no mundo real" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para tdd-no-mundo-r...@googlegroups.com.
Para mais opções, acesse https://groups.google.com/groups/opt_out.



--

Alexandre Gama

unread,
Mar 6, 2014, 3:35:19 PM3/6/14
to tdd-no-m...@googlegroups.com
Detalhe: Acho ainda mais elegante pelo construtor pelo fato de você conseguir indicar que aquele objeto realmente não sobrevive sem o Session

@Inject
public System(SqlSession session) {
   this.session = session

Só injetar seu mock :)

Henrique Meira

unread,
Mar 6, 2014, 4:15:57 PM3/6/14
to tdd-no-m...@googlegroups.com
Imaginei que teria que ser assim mesmo.

Vou reavaliar o design e criar os construtores.

henrique.

Henrique Meira

unread,
Mar 6, 2014, 7:32:52 PM3/6/14
to tdd-no-m...@googlegroups.com
Olha só. Pensando bem, acho que criar um construtor apenas para que meus testes funcione não seria correto. Arrisco a dizer que acaba ferindo o princípio do aberto-fechado, acoplando os objetos. 

Estou pensando em solucionar isto de outra maneira. No caso, pensei em criar uma classe de teste que estenderá a minha classe a ser testada, e nesta sim, crio um construtor para receber o dito SqlSession. 

Penso que assim não altero minha classe de produção, e mantenho o teste no ambiente de teste.

O que acham?

henrique.

Marcus Alexandre Silva

unread,
Mar 6, 2014, 7:49:25 PM3/6/14
to tdd-no-m...@googlegroups.com
import static org.springframework.test.util.ReflectionTestUtils.*;

SystemModel  systemModel = new SystemModel();
setField(systemModel, "session", new SqlSession());
assertNotNull(systemModel.getMenuItems());


Para mais opções, acesse https://groups.google.com/d/optout.

Mário Meyrelles

unread,
Mar 6, 2014, 8:11:01 PM3/6/14
to tdd-no-m...@googlegroups.com
Além da opção que o Marcus deu (não entendo java o suficiente para discutir), em C#, você pode criar um construtor que seja internal, ou seja, só visto pelos objetos que estão no mesmo assembly (dll, pacote).


Henrique Meira

unread,
Mar 6, 2014, 8:22:48 PM3/6/14
to tdd-no-m...@googlegroups.com
Excelente Marcus. Obrigado.

Henrique Meira

unread,
Mar 6, 2014, 8:23:54 PM3/6/14
to tdd-no-m...@googlegroups.com
Oi Mário, acho que o equivalente seria modificar a propriedade para "protected", assim, os pertencentes do pacote teriam acesso.

É mais uma saída.

Obrigado.

henrique.

Alexandre Gama

unread,
Mar 6, 2014, 8:27:54 PM3/6/14
to tdd-no-m...@googlegroups.com
Euuu particularmente não gosto da ideia de estender a classe pra fazer testes e também não gosto do protected. 

Sobre o setField com o spring acho interessante mas mesmo assim gosto da saída do Construtor. Não acho que
estou colocando o construtor para ajudar meu teste e sim ao contrário, estou indicando que minha classe não consegue
nascer sem uma Session e consequentemente facilita meu teste. 

Faz sentido?

Abs!

Marcus Alexandre Silva

unread,
Mar 6, 2014, 8:44:09 PM3/6/14
to tdd-no-m...@googlegroups.com
Xará, beleza?

Isto que você esta falando faz todo o sentido, se estivéssemos trabalhando com o programação crua, livre de aspectos, como o caso do "@Inject".

Justamente por ter este facilitador de injeção de dependência são necessárias algumas artimanhas  para tornar os testes igualmente fáceis de serem escritos e mantidos, neste ponto concordo com o Henrique quando ele diz "criar um construtor" para fazer com que o teste funcione.

Bom mesmo seria se ele estivesse usando algum mecanismo de mapeamento do Container IoC para seus testes, apontando e iniciando as classes mocadas em tempo de inicialização dos testes unitários... 

Ai não precisava nem de construtor e nem de injeção via reflection, só do mapeamento especial dos testes....  

Att,

Marcus Alexandre

Mário Meyrelles

unread,
Mar 6, 2014, 8:52:13 PM3/6/14
to tdd-no-m...@googlegroups.com
Olá Henrique, 

Por curiosidade achei no SO que não é protected e sim, a omissão do public/private/protected que deixa a classe visível apenas a package dela. Acredito que o protected faça sentido em situações de herança e classes abstratas, que pode vir a ser o caso no seu exemplo.

Mário Meyrelles

unread,
Mar 6, 2014, 9:06:36 PM3/6/14
to tdd-no-m...@googlegroups.com
É então,

Acho que na prática, o que queremos, no fim do dia, é ter só um construtor, público, com o parâmetro ISession como  parâmetro obrigatório.

No fim do dia, o que se quer é ter uma classe que claramente mostre para o usuário dela que ela só consegue existir recebendo uma instância de um ISession. Logo, concordo com o entendimento de que ter um construtor formal é mais do uma artimanha para testes com DI - é uma necessidade real do seu modelo!

Relendo a thread e pensando no mundo real, a solução de criar um construtor só para testes atende só em casos mais específicos. Em .NET, usando WPF, muita gente usa esta técnica para injetar uma ViewModel fake para que, em tempo de design, seja possível projetar como ficaria a tela com dados de teste. Bem legal! Usando outro caso do dia-a-dia em .NET, quando se usa DI, os controllers do ASP.NET MVC recebem suas dependências via injeção mesmo. E não há construtores sem parâmetros.


PS: Jamais injetar em propriedades ou fields



Mauricio Aniche

unread,
Mar 7, 2014, 8:58:10 AM3/7/14
to tdd-no-mundo-real
Oi Henrique,


Olha só. Pensando bem, acho que criar um construtor apenas para que meus testes funcione não seria correto. Arrisco a dizer que acaba ferindo o princípio do aberto-fechado, acoplando os objetos. 

Eu acho que é exatamente o contrário. Quando você recebe dependências pelo construtor, aí sim que você desacopla, e favorece o princípio do aberto-fechado.

Se você dá "new" em um objeto dentro da classe, você NUNCA consegue trocá-lo em tempo de execução. Ao passo que se você recebe no construtor, você consegue facilmente trocar a implementação que será usada por aquela classe, possibilitando a extensão daquela classe, sem a necessidade de por a mão no código dela.

Eu não vejo problema algum em escrever código a mais para facilitar a testabilidade. Escrevemos código "a mais" o tempo todo, seja pra colocar um getter pra facilitar a view, seja pra criar algo que ajude na infraestrutura. Acho uma troca bastante justa, criar um construtor para uma classe para facilitar o teste. O ganho é maior.

Outra coisa: pelo que a classe recebe (uma SqlSession), você tem certeza que quer mockar isso? Um teste de integração não faz mais sentido?

Um abraço,

Henrique Meira

unread,
Mar 7, 2014, 9:52:33 AM3/7/14
to tdd-no-m...@googlegroups.com
Antes de mais nada, agradeço a todos pela valiosa colaboração à este tópico.

Maurício, entendo seu ponto de vista. Alias voltei ao livro, mais especificamente "8.3 Dependências Explícitas".

O que ocorreu aqui foi a dúvida em trabalhar com CDI. Gostaria de conhecer as práticas utilizadas pelos programadores/arquitetos e descobri que tudo não passa de uma questão de bom senso e harmonia com o projeto e equipe.

Com relação ao Mock, talvez tenha entendido errado, eu não estou utilizando mocks. Meu teste é integrado sim, acesso direto ao banco.

Grato.

henrique.

Mauricio Aniche

unread,
Mar 7, 2014, 12:00:30 PM3/7/14
to tdd-no-mundo-real
Oi,

Maurício, entendo seu ponto de vista. Alias voltei ao livro, mais especificamente "8.3 Dependências Explícitas".

Mas concorda com ele? :)

Pq, adoraria discutir sobre isso, caso não concorde. 

O que ocorreu aqui foi a dúvida em trabalhar com CDI. Gostaria de conhecer as práticas utilizadas pelos programadores/arquitetos e descobri que tudo não passa de uma questão de bom senso e harmonia com o projeto e equipe.

Bom senso é a palavra chave para muita coisa, desde escrever bons testes até alcançar a paz mundial.
 
Com relação ao Mock, talvez tenha entendido errado, eu não estou utilizando mocks. Meu teste é integrado sim, acesso direto ao banco.

Ufa! :) 

Um abraço,
Reply all
Reply to author
Forward
0 new messages