Testes unitários em repositórios?

Saudações!

Uma pergunta simples:

Faz sentido testar unitariamente meus repositórios?


class UserRepositoryTest {

      UserRepository uRep;

      @Before
      public void setUpRepository() throws Exception {
           uRep = //Cria uma instancia Mock do repositório...
      }

      @Test
      public void testAddUser() throws Exception {
           User user = new User();
           // "Setta" os atributos do usuario..
          uRep.add(user);
      }

      // Outros métodos de teste...

}

Não é perda de tempo fazer esse tipo de teste? Pois, caso ele falhe, isso vai depender da implementação do meu repositório e não da interface em si. Se minha implementação do repositório persistir esses dados em file system posso receber um FileNotFoundException (encapsulado para uma exceção de negócio, ou mais genérica como UnableToAddUserException).

Quando os testes são assim tão simples, mesmo assim vale a pena ter um test case? Quais as vantagens dessa abordagem?

[quote=Danilo Barboza]Saudações!

Uma pergunta simples:

Faz sentido testar unitariamente meus repositórios?


class UserRepositoryTest {

      UserRepository uRep;

      @Before
      public void setUpRepository() throws Exception {
           uRep = //Cria uma instancia Mock do repositório...
      }

      @Test
      public void testAddUser() throws Exception {
           User user = new User();
           // "Setta" os atributos do usuario..
          uRep.add(user);
      }

      // Outros métodos de teste...

}

Não é perda de tempo fazer esse tipo de teste? Pois, caso ele falhe, isso vai depender da implementação do meu repositório e não da interface em si. Se minha implementação do repositório persistir esses dados em file system posso receber um FileNotFoundException (encapsulado para uma exceção de negócio, ou mais genérica como UnableToAddUserException).

Quando os testes são assim tão simples, mesmo assim vale a pena ter um test case? Quais as vantagens dessa abordagem?[/quote]

Concordo com vc. Se nao tem regra de negocio na implementacao do repositorio (o que e normal IMO) realmente nao ha o que testar no repositorio. O comportamento da persistencia é testado num outro nivel de teste, que nao sao unitarios. Isso nao quer dizer que a persistencia sera ignorada pelos objetos de dominio, eles apenas nao conhecem a implementacao do repositorio que podera entao ser preparada para cenarios de testes especificos onde ambientes sao simulados para testar cada regra de negocio isoladamente. Contando ou nao com ajuda de objetos mock.

Se ao invés de mockar o repositório você fizer um teste de integração, efetivamente gravando dados no banco de dados e verificando se tudo é salvo/editado/excluido como deveria, você vai ter um feedback muito mais relevante pra esse teste. Dá um pouco mais de trabalho pra fazer setup/teardown, mas evita bastante dor de cabeça na hora de pegar bugs na camada de persistência.

Sim. Faz todo o sentido. Mas nesse caso não seria mais teste unitário e sim de integração. Minha dúvida é realmente sobre testes unitários. Então não seria muito vantajoso ter um teste unitário em repositório, correto?

Valeu pelas respostas s4nchez e cmoscoso!

Se UserRepository é uma interface, não sei por que fazer teste unitario se não existe código nela pra testar. Eu no seu lugar testaria cada implementação em um teste diferente e não a interface comum nessa única classe.

Cara… Isso faz todo o sentido!! hehehe…

O que vc está me dizendo, então, é que Mockar algo só lhe é útil em testes de integração, certo? (e isso faz completo sentido agora… tá, galera… eu ainda não tinha percebido… Quem vai atirar a primeira pedra? ;))

Isso pareceu um insight meio besta… Mas me deu uma clareada legal nas coisas!!

Valew, renato3110!

E aquela história de que “Os testes devem x (onde x>1) linhas de código a mais do que o código em si (aquilo que será testado)”? Isso não é meio parecido com “Você produziu bem hoje pois você produziu n (onde n é um número que seu chefe julga produtivo) linhas de código!”? Ou estou viajando?

Até!

Danilo,

Você deve usar o Mock dela quando você for fazer os testes unitários dos modelos que a usam. Agora no caso de testar diretamente o repositório (e suas implementações) é mais fácil (e faz mais sentido) você criar teste de integração (como o Sanchez disse) com o banco de dados. Ah, lembre-se de deixar uma base de dados exclusiva para testes, onde você conhece o estado inicial dos valores e sabe quais os dados finais que você deve esperar.

So completando,
metodos sem logica não precisam ser testado unitariamente. Se precisasse nos iamos acabar testando gets/sets :D.
Para testes de integração com o BD, use o DBUnit para isso.

[]´s

[quote=Danilo Barboza]

O que vc está me dizendo, então, é que Mockar algo só lhe é útil em testes de integração, certo?[/quote]

Nao se usa mocks em teste de integracao!!!

E nao faz sentido um teste unitario do objeto que esta mockado… :shock:

De uma pesquisada sobre mocks para entender qual o seu papel no teste de sw.

É, acho que me faltam alguns conceitos…

Tpw, teste unitários e testes de integração… Por mais que tenha lido diversos artigos e papers por aí sobre o assunto as coisas ficam meio confusa quando tentamos colocar em prática…

O tópico do Bruno está muito interessante e pertinente. Na verdade, acho que essa é a pergunta que deveria ser feita “Como começo a fazer testes” e “Como tornar isso um hábito”.

As respostas de vocês foram muito esclarecedoras. Essa é a idéia de um fórum de discussão, discutir conceitos mal entendidos até se chegar em um ponto onde os conceitos fiquem mais claros!!!

Valew, galera!

Oi,
mesmo assim fica interessante deixar a interface, pois voce pode estar testando diferentes classes apenas alterando uma e outra configuracaozinha num descriptor… isso facilita, por exemplo, para testar implementacoes novas …

Sim, e?

alterei a mensagem para ficar entendível.

Entendi, mas como ficaria o teste da implementação em si?

Se uma interface possui mais de 1 implementacao, meu alvo no teste sao cada implementacao. Eu prefiro dar ao teste o nome da implementacao + sufixo test, assim deixo explicito quem esta sendo testado. No caso de uma classe abstrata eu a testaria se houvesse algum comportamento sendo implementado nela, e nao testaria novamente nas implementacoes, a nao ser que o comportamento seja redefinido por alguma das subclasses.

Oi, assim, testes não sao feitos apenas invocando o método a ser testado e um ou dois asserts, testes bem escritos, na verdade, possuem toda uma lógica do que se esta testando, do tipo:

[code]testando a verificao: verificaSeProfessorJaEstaEmAula(professor, horario)

cadastra uma sala de aula
cadastra uma professor
coloca o professor na sala
cadastra outra sala de aula
tenta colocar o mesmo professor nesta outra sala de aula no mesmo tempo
verifica se da pau[/code]

Imagina criar uma classe de teste para cada implementação? Então você apenas inverte o controle da interface do teste e faz ela te apresentar em qual implementação houve um problema, se houver. Isso tu pode fazer durante o build de uma integração continua, por exemplo.

abs

Outra coisa, testar um insert assim:

insert(bean);

não faz nenhum sentido… o teste eficaz para isso, seria:

[code]- pega a quantidade de registros atuais na var A

  • insere(bean)
  • pega a quantidade de registros atuais na var B
  • assertTrue(B > A)[/code]

[quote=peerless]Imagina criar uma classe de teste para cada implementação? Então você apenas inverte o controle da interface do teste e faz ela te apresentar em qual implementação houve um problema, se houver. Isso tu pode fazer durante o build de uma integração continua, por exemplo.

abs[/quote]

E todas as funcionalidades específicas da implementação, vão ficar sem testes?

[quote=renato3110][quote=peerless]Imagina criar uma classe de teste para cada implementação? Então você apenas inverte o controle da interface do teste e faz ela te apresentar em qual implementação houve um problema, se houver. Isso tu pode fazer durante o build de uma integração continua, por exemplo.

abs[/quote]

E todas as funcionalidades específicas da implementação, vão ficar sem testes?[/quote]

Bingo! Lembre-se que interfaces esperam pordefinições de contratos. Devemos esperar que todas as implementações de uma API atendam aos meus contratos, senão há uma falha, no nosso caso, entre uma implementação e outra. Resumidamente falando: O código deve se manter funcionando não importando qual implementação utilizar.

Mas o teste unitario deve testar todas as implementacoes. Nao do ponto de vista da interface do objeto, mas a implementacao da responsabilidade, que geralmente é 1:1 se estiver seguindo o SRP.