Classes testáveis não “buscam”, mas sim “recebem”

TL;DR: Se você quer testar sua classe por meio de teste de unidade, essa classe não deve conter código de infra estrutura (como acesso a banco de dados, e etc), e ela também deve receber toda outra informação ou dependência necessária por meio de construtores ou parâmetros de métodos — a classe nunca deve buscar a informação diretamente ou instanciar uma dependência. Dessa forma, ela será facilmente testável por meio de testes de unidade.

Regras de negócio, no fim, nada mais são do que um monte de ifs e fors juntos. Portanto, deveríamos ser capazes de sempre testá-las por meio de simples testes de unidade. No entanto, às vezes nós dificultamos isso.

Veja o código abaixo. Nele, o FiltroDeFatura pega a lista de todas as faturas que está no banco de dados, e passeia por elas, guardando as que tem valores menor que 2000.

O código é simples, mas pense que ele poderia ser um pouco mais complicado. Portanto, precisamos testá-lo. A questão é: como? Afinal, não conseguimos escrever um teste de unidade pra ela; afinal é impossível executar o método filtra() sem passar por um banco de dados.

Essa é um tipo de código bastante comum nas aplicações por aí. O desenvolvedor sabe que não pode sair misturando SQL no meio de regra de negócio, e corretamente, coloca isso dentro do DAO. No entanto, ele instancia o DAO diretamente na classe, fazendo com que não seja possível executar a regra de negócio, sem passar pelo banco de dados.

E passar pelo banco de dados, na maioria das vezes, não é boa ideia. Escrever o teste é mais difícil (afinal, precisamos fazer INSERT dos dados no começo, DELETE depois, garantir o schema do banco, e etc), e mais demorado. E, aliás, não deveríamos precisar do banco de dados, para testar a simples regra de filtro de fatura.

A solução pra isso é mais fácil do que parece. Se você tem uma classe que contém regras de negócio, essa classe deve apenas conter regras de negócio. Ou seja, ifs e fors. Se sua regra de negócio precisar de alguma informação que venha de uma outra classe qualquer (seja um DAO, seja outra coisa), ela nunca deve “buscar” essa informação, mas sim “recebê-la”.

Ou seja, nesse código em particular, ou passamos a List<Fatura> para o método filtra(), ou passamos o FaturaDao pelo construtor. Veja:

Dessa forma, com o DAO sendo recebido pelo construtor da classe, conseguimos simular seu comportamento durante o teste, por meio de mock objects. E agora sim, nada de depender do banco para fazer um teste tão simples.

Portanto, guarde essa regra: se sua classe é uma classe que contém regras de negócio, ela nunca pode buscar as informações ou dependências que precisa por conta própria; ela deve sempre recebê-las.

Ah, essa ideia tem um nome bonito, inclusive: chama-se inversão de controle. Ou seja, “invertemos” a maneira tradicional de programar, que é sempre buscar pela dependência. Agora, alguém nos dá a dependência. Você pode ler mais sobre isso no meu livro de orientação a objetos e SOLID.

5 thoughts on “Classes testáveis não “buscam”, mas sim “recebem”

  1. Itacir

    Depois de ler alguns livros seus, sua dicertação de metrodo, escrever artigos sobre TDD e BDD na faculdade, vejo quanto podereso é o conceito de inversão de depencia, uma dica que vale objservar é no mundo Java é de nos beneficiar é muito do CDI para essa terafa ele faz isso e uma forma muito fluente, muito bom post Mst Anishe mais uma vez ajudando o devs.

    Reply
  2. Anna

    A dúvida que me surge é como fazer isso em MB’s que em tese são a cola da tela e falam com bc’s ou dao’s diretamente, eles deveriam também receber pelo construtor? Em geral eu acabo por injetar a dependência do bc….

    Reply
    1. mauricioaniche Post author

      Oi Ana. MBs são managed beans? Se vc tem uma infra que te atrapalha, a ideia é você fugir dessa infra, ter seu modelo bonito, isolado e testado, e depois converter pra sua infra chata que não te deixa testar direito.

      Entendeu?

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *