Como efetuar TDD na camada Service – hibernate mvc tdd

Pergunta:


Participo de um projeto que utiliza camadas MVC com framework Hibernate persistindo em um Postgres. Para testes utiliza-se o Junit e para mock o Mockito (ainda não tenho conhecimento e prática sobre o mesmo)

Então queria saber, como vocês trabalham o TDD com a camada service, por exemplo como você validam Hibernate Annotations.

O que você fazem e qual a experiência que podem compartilhar?

Autor da pergunta Macario1983

Resposta :

TDD ou Teste Unitário?

Existe uma grande diferença entre TDD e Teste Unitário. O TDD tem como base testes unitários, mas ambos conceitos não são exatamente sinônimos.

TDD (Test Driven Development) é uma metodologia de desenvolvimento que mede o progresso do projeto de acordo com os resultados dos testes.

Teste Unitário é um dos tipos de teste que se costuma fazer em software. Existem ainda testes de integração, de sistema, de usuário, de carga, de desempenho e outros.

Um teste unitário deveria testar um único cenário de um método do sistema sem depender de recursos externos, como bancos de dados, configurações e outros fatores que possam interferir no resultado. Em resumo, o teste unitário deve testar somente uma coisa.

TDD na prática

O TDD funciona bem para alguns tipos de projetos, principalmente quando os requisitos estão bem definidos e é possível escrever cada o teste antes da implementação. É o caso ideal.

Na prática, entretanto, muitos dos requisitos vão evoluindo no decorrer do projeto, tanto por mudanças nos negócios, quanto por falha na elicitação e mesmo pela maturação do entendimento dos usuários. Este e outros motivos inviabilizam um TDD “puro” porque ele acrescenta um overhead de esforço muito grande e muitas equipes não podem se dar a esse luxo.

Outro fator que atrapalha é a maturidade da equipe. Não adianta ensinar um junior a usar JUnit e achar que ele vai conseguir fazer TDD. Não é fácil criar um sistema testável, que é uma das qualidades de uma boa arquitetura do software.

O importante é entender que TDD não é uma bala de prata .

Testes unitários na prática

Teste as funcionalidades importantes

O que tenho visto no mercado e que tem dado certo é focar o teste naquilo que é importante. Não há tempo de testar tudo, então o foco deve ser nas funcionalidades centrais do sistema e não em cadastros (CRUD).

Muitas pessoas tem dúvidas sobre o que testar, algumas sugerem até testar rotinas do framework ou, no caso de Java, verificar se o Hibernate está salvando os dados no banco. Isso deixa o desenvolvedor louco e vai na direção errada, pois no final ele vai testar coisas que deveriam estar funcionando e deixar de lado as mais importantes.

Teste unitário, mas não tão unitário

O teste não precisa ser exatamente unitário, no sentido de testar apenas um método.

Trabalhei num projeto que envolve integração com mais de meia dúzia de sistemas. São integrações para todos os gostos: Web Services, arquivos, bancos de dados. Muitos dos sistemas ainda estão em processo de mudança. Seria inviável criar mocks para tudo e ter que atualizar os mocks cada vez que um sistema mudasse.

Então, para citar um exemplo de teste de importação de arquivo, eu criei um método JUnit que executa a tarefa (job) de importação e verifica se o registro foi armazenado com sucesso. Embora acabe testando muita coisa de uma vez só, isso está sendo suficiente para garantir a funcionalidade.

Não é “pecado” acessar o banco de dados em teste unitário. O ruim é ter o teste falhando frequentemente caso as tabelas não estejam no estado necessário para a correta execução. Mas nada impede de inicializar valores no setup do teste. Ou ainda há a opção de usar um framework como TestNG, onde você pode definir a ordem de execução. Na verdade, não é exatamente a ordem, o que você pode definir é que um método de teste X depende do teste Y ter executado antes. Particularmente gosto muito do TestNG.

O design conta muito

Falando especificamente sobre sua camada de serviços, posso repassar as lições que a experiência me trouxe.

O mais importante é sempre projetar suas classes e métodos para que sejam testáveis. No começo é difícil e você deve gastar um tempo com refactoring. Faça com que cada método ou rotina importante seja o menos acoplado possível com outras rotinas e configurações estáticas. Abuse da Inversão de Controle.

Acompanhe um exemplo que darei no próximo tópico

Um exemplo fictício

Vamos criar um classe responsável pela importação de um arquivo. Suponha que a primeira implementação seja bem ingênua:

public class ImportadorArquivo {
    public void importar() {

        //carrega local da configuração
        File arquivo = new File(Configuracao.LOCAL_ARQUIVO);

        //lista com itens lidos do arquivo
        List<Entidade> entidadesLidas = new ArrayList<>();

        //vários comandos para ler e interpretar o arquivo, colocando itens na lista...
        String[] linhas = FileUtil.lerLinhas(arquivo);
        for (String linha : linhas) {
            Entidade e = new Entidade();
            //preenche entidade com os dados da linha...
            entidadesLidas.add(e);
        }

        //grava itens no banco
        for (Entidade e : entidadesLidas) {
            JPAUtil.getEntityManager().persist(e);
        }

    }
}

É um método bem ruim de testar, certo? Vamos refatorar essa classe para deixá-la mais testável:

public class ImportadorArquivo {

    private File arquivo;
    private EntityManager em;

    //recebe arquivo em entity manager (IoC)
    public ImportadorArquivo(File arquivo, EntityManager em) {
        this.em = em;
        this.arquivo = arquivo;
    }

    public void importar() {
        List<Entidade> entidadesLidas = ler();
        salvar(entidadesLidas);
    }

    public List<Entidade> ler() {
        List<Entidade> entidadesLidas = new ArrayList<>();
        String[] linhas = FileUtil.lerLinhas(arquivo);
        for (String linha : linhas) {
            entidadesLidas.add(interpretar(linha));
        }
        return entidadesLidas;
    }

    public Entidade interpretar(String linhaArquivo) {
        Entidade e = new Entidade();
        //preenche entidade com os dados da linha...
        return e;
    }

    public void salvar(List<Entidade> entidadesLidas) {
        for (Entidade e : entidadesLidas) {
            em.persist(e);
        }
    }

}

Note que agora cada método possui uma ação bem definida e distinta. Isso permite testar cada ação individualmente.

Perceba também que a classe recebe as configurações por parâmetro. Isso é um tipo de Inversão de Controle. Isso permite você testar a classe sem precisar de framework ou mágica alguma.

Imagine o potencial de um bom design se aplicado a todo o sistema?

Exemplos reais

Tenho trabalhado em algumas pequenas bibliotecas e frameworks usando TDD, pelo menos até certo ponto.

Vou listar aqui dois projetos recentes e atualizados no GitHub com aproximadamente 90% de cobertura de testes unitários:

  • MyQ: uma biblioteca para organizar, carregar e processar queries SQL em um projeto Java.
  • T-Rex: um gerador de planilhas Excel através de templates e uma expression language.

Embora eu saiba que ainda tenho muito a aprender e melhorar em minhas implementações, sugiro que é um bom exercício olhar o design das classes nesses projetos e como eles permitem que os testes unitários sejam executados sem nenhum framework de mock.

Leitura adicional

Para uma discussão interessante sobre o assunto, veja meu artigo O TDD está morto? e, se entender Inglês, assista o vídeo Is TDD Dead?.

Fonte

Related Posts:

Qual a diferença entre AppCompatActivity e Activity? – android android-activity
Pergunta: Qual a diferença da AppCompatActivity para Activity ? A partir de qual versão a AppCompatActivity foi adicionada ao Android? Autor da pergunta Luhhh A diferença reside ...
Como abreviar palavras em PHP? – php string
Pergunta: Possuo informações comuns como nome de pessoas e endereços, e preciso que elas contenham no máximo 30 caracteres sem cortar palavras. Exemplo: 'Avenida Natalino João Brescansin' ...
Qual é a finalidade de um parêntese vazio numa declaração Lambda? – c# expressões-lambda característica-linguagem
Pergunta: Criei um exemplo de uma declaração Lambda sem argumentos, entretanto, estou com duvidas referente a omissão do parêntese vazio () na declaração. Veja o exemplo: class ...
Boas práticas para URI em API RESTful – api rest restful
Pergunta: Estou com dúvida em relação às URIs de alguns recursos da api que estou desenvolvendo. Tenho os recursos projetos e atividades com relação 1-N, ...
Dúvidas sobre a integração do MySQL com Java – java mysql netbeans
Pergunta: Estou criando um sistema no NetBeans, utilizando a linguagem Java e o banco de dados MySQL. Escrevi o seguinte código para realizar a conexão ...
Qual é a finalidade da pasta Model do framework Inphinit? – php inphinit
Pergunta: No Inphinit micro-framework existe a pasta Model que fica dentro da pasta application, e nela é onde ficam as classes, mas eu estou muito ...
Uso do ‘@’ em variáveis – javascript typescript coffeescript
Pergunta: Vejo em algumas linguagens que compilam para javascript, como TypeScript e CoffeeScript, o uso do @ em variáveis, como também, casos em que o ...
Qual tamanho máximo um arquivo JSON pode ter? – json arquivo
Pergunta: Vou dar um exemplo para conseguir explicar minha duvida: Preciso recuperar informação de imagens vindas de uma API, esse banco de imagens me retorna JSON's ...
O que é Teste de Regressão? – terminologia engenharia-de-software testes
Pergunta: Na matéria de Teste de Software o professor abordou um termo chamado Teste de Regressão, isto dentro da disciplina de teste de software. Sendo ...
O que é um construtor da linguagem? – php característica-linguagem
Pergunta: Em PHP, já li e ouvi várias vezes a respeito dos Construtores da Linguagem. Os casos que sempre ouvi falar deles foi em casos ...
Função intrínseca para converter numérico para string – cobol
Pergunta: Estou a tentar saber se existe alguma função intrínseca do COBOL para converter um data numérico para string sem precisar usar a cláusula REDEFINES: ( ...
Porque usar implements? – java android
Pergunta: Qual a diferença entre usar btn.setOnClickListener(new OnClickListener() { e public class MainActivity extends Activity implements OnClickListener{ Estive fazendo um curso de Android e meu professor falou que ...
O que é XHTML e quando deve ser usado? – html xml xhtml
Pergunta: O que eu sei é que o XHTML precisa ser XML válido. Isso implica, por exemplo, que todas as tags precisam ser fechadas. Por ...
Uma placa aceleradora de vídeo pode melhorar o desempenho não-gráfico? [fechada] – desempenho
Pergunta: Para desenvolver em Ruby on Rails, eu utilizo aqui uma máquina virtual do VirtualBox com Ubuntu Server 14.04 sem interface gráfica instalada. Recentemente descobri uma ...
Concat() VS Union() – c# .net
Pergunta: Qual a diferença entre Concat() e Union() ? Quando usar Concat() e quando usar Union() ? Somente pode ser usado em list ? ...

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *