Você escreve testes de unidade o tempo todo no TDD?


8

Estou projetando e desenvolvendo código com o estilo TDD há muito tempo. O que me incomoda no TDD é escrever testes para código que não contém nenhuma lógica comercial ou comportamento interessante. Sei que o TDD é uma atividade de design mais do que testar, mas às vezes sinto que é inútil escrever testes nesses cenários.

Por exemplo, eu tenho um cenário simples como "Quando o usuário clica no botão de verificação, ele deve verificar a validade do arquivo" . Para esse cenário, geralmente começo a escrever testes para a classe apresentador / controlador, como a abaixo.

@Test
public void when_user_clicks_check_it_should_check_selected_file_validity(){
    MediaService service =mock(MediaService);
    View view =mock(View);

    when(view.getSelectedFile).thenReturns("c:\\Dir\\file.avi");

    MediaController controller =new MediaController(service,view);
    controller.check();

    verify(service).check("c:\\Dir\\file.avi");
}

Como você pode ver, não há decisão de design ou código interessante para verificar o comportamento. Estou testando valores da exibição passada para o MediaService. Normalmente escrevo, mas não gosto desse tipo de teste. O que você faz sobre essas situações? Você escreve testes o tempo todo?

ATUALIZAÇÃO:

Alterei o nome e o código do teste após as reclamações. Alguns usuários disseram que você deve escrever testes para casos triviais como esse, para que no futuro alguém possa adicionar um comportamento interessante. Mas e quanto a "Código para hoje, design para amanhã". ? Se alguém, inclusive eu, adicionar um código mais interessante no futuro, o teste poderá ser criado para ele. Por que devo fazer isso agora para os casos triviais?


1
"Alguns usuários disseram que você deve escrever testes para casos triviais como este, para que no futuro alguém possa adicionar comportamentos interessantes. Mas e quanto a" Código para hoje, design para amanhã ". ? "Refutação brilhante! +1
maple_shaft

Respostas:


5

Não pretendo 100% de cobertura de código. E normalmente não escrevo testes de métodos que obviamente não conterão nenhuma lógica comercial e / ou mais do que algumas linhas de código. Mas ainda escrevo testes de unidade (usando TDD) de métodos que não parecem tão complexos. Isso ocorre principalmente porque eu gosto de fazer o teste de unidade já, ao voltar a esse código meses ou mesmo anos depois, e quero torná-lo mais complexo. É sempre mais fácil estender os testes existentes, do que ter que construir tudo do zero. Como Noufal disse, é subjetivo. Meu conselho é escrever os testes, se você acha que o método é um pouco complexo ou tem potencial para se tornar mais complexo.


2

Esta é a segunda pergunta do TDD hoje que transmite aborrecimento ao número de testes a serem escritos.

"Teste apenas se você quiser que funcione."

Não tenho muita certeza de entender o teste da pergunta.

Você está verificando se Controller.Check () delega para o serviço (dependência) com o argumento como o valor do arquivo selecionado na exibição? Se sim, este é um bom teste. Permite testar o controlador sem a implementação real do serviço. (micro-testes baseados em interação).

Atualização: Agora que estou claro sobre o que você está tentando testar, provavelmente mudaria algum código e renomearia algumas coisas para que se lê "Test Media Controller delega verificação de arquivo selecionada ao serviço de mídia". - que é uma especificação válida para o controlador.

public class TestMediaController

@Test
public void DelegatesSelectedFileCheckToMediaService(){
    string selectedMediaFileInView = "c:\\Dir\\file.avi";

    when(_view.getSelectedFile).thenReturns(selectedMediaFileInView);

    new MediaController(_service, _view).check();

    verify(_service).check(selectedMediaFileInView);
}

Sim Delega para a classe de serviço.

Mas pergunta: vale a pena escrever este teste para verificar apenas os parâmetros passados ​​da visualização para a classe de serviço?

@ Sim - ele testa se check () está conectado corretamente. Deve haver outro teste para que Service.Check () funcione assim que receber a chamada. Esse teste parece trivial hoje, mas com o passar do tempo alguém pode adicionar mais código a esse método (por exemplo, uma cláusula de guarda se alguma condição retornar;) e interromper o comportamento existente. Este teste protege contra isso.
Gishu

Ok, entendi seu argumento, mas e o “Código para hoje, design para amanhã”. ? Se alguém ou eu adicionarmos um código mais interessante no futuro, posso criar um teste para ele. Por que devo fazer isso agora para os casos triviais?

1
@mcaaltuntas: o ponto principal do TDD é que você testa a especificação atual, para que, se um código futuro não mais satisfizer a especificação atual, você descubra. Não importa o quão fácil você pense em implementar as especificações agora, alguém poderá alterar o código no futuro sem prestar atenção a esse requisito específico; nesse caso, eles poderão quebrá-lo e não perceberão que precisam adicionar um teste para ele. . Se você não escrever o teste agora, não estará fazendo TDD. Se você não quiser testar toda a especificação, a decisão é sua, mas você não está aplicando o TDD a essa parte.
21711 Steve Jobs (

1

Eu não escreveria um teste assim (ou pelo menos não o nomearia assim). Em vez disso, eu escreveria um teste para o recurso que requer essa chamada check(), para que, se essa verificação ou uma ação equivalente não for feita, o recurso de alto nível não funcione. Por que seu código precisa chamar o check()método?

Em geral, tento manter os testes dissociados dos detalhes da implementação, para que pelo menos o nome do teste fale apenas sobre os recursos externos fornecidos pelo objeto. Detalhes de implementação, como objetos e métodos, não são mencionados no nome do teste.

Isso facilita a refatoração (não deve ser necessário alterar os testes quando você altera a implementação) e também facilita descobrir se um teste está desatualizado (o recurso especificado não é mais necessário) . Também facilitará a observação de códigos desnecessários / inoperantes, porque o clichê de baixo nível (como getters e setters) será adicionado / mantido apenas se forem exigidos por recursos de nível superior.


Ok. Você está certo sobre nomear, pode pensar em seu nome "quando o usuário clica em verificar, deve verificar as propriedades do arquivo selecionado". Mas a questão não é sobre a nomeação Do que você escrever testes para o código "óbvio" ou não

Mudei o nome do teste, mas a pergunta ainda permanece.

O que o sistema deve fazer, se essa verificação falhar? Lançar uma exceção ou outra coisa? Eu provavelmente escreveria dois testes: o que acontece quando o arquivo é válido e quando é inválido. Se houver muitas maneiras de um arquivo ser inválido, eu escreveria esses testes diretamente no MediaService.
Esko Luontola

Sim Probaby Eu escreveria dois testes também, mas não contra a classe Controller Eu os escreveria contra (como você disse) a classe MediaService. Portanto, na verdade, o teste apenas verifica se a classe de serviço foi chamada com parâmetros de visualização. Você acha que vale a pena escrever esse teste para verificar apenas os argumentos passados ​​da exibição para a classe de serviço?

"Você acha que vale a pena escrever este teste apenas para verificar os argumentos passados ​​da exibição para a classe de serviço?" Depende. Se houver testes de ponta a ponta, que garantam que a verificação funcione (ou seja, transmite uma mensagem de erro ou algo quando o arquivo não é válido), para os testes de unidade do controlador, pode ser suficiente apenas verificar se o método é chamado; daria proteção mais rápida durante a refatoração do que os testes de ponta a ponta.
Esko Luontola

0

Isso é subjetivo. Eu nem sempre faço TDD, mas quando faço, tento manter a cobertura do código como minha métrica para saber se meus testes são abrangentes ou não. Às vezes fico preguiçoso e simplesmente pulo partes que, para mim, parecem "óbvias". Às vezes, violo o ciclo Vermelho, Verde, Refatorador e escrevo mais código do que o necessário, mas, com o tempo, entrei em um ritmo do que me sinto confortável.


0

Interações entre classes como a anterior e o original mais simples para as quais eu escreveria um teste. As interações podem se tornar mais complexas ao longo do tempo, portanto, é bom ter o terreno no local.


0

Se alguém ou eu adicionarmos um código mais interessante no futuro, posso criar um teste para ele. Por que devo fazer isso agora para os casos triviais?

Você assume que, no futuro, alguém saberá que esse elemento da interface do usuário existe e o que ele chama no back-end.

Meu projeto atual tem mais de 30 desenvolvedores em seis equipes distintas dentro da mesma base de código. Adições triviais da interface do usuário desaparecem no nevoeiro o tempo todo. Ninguém voltará e incluirá um caso de teste para isso mais tarde, porque ninguém se lembrará de que está lá e, quando isso quebra, a demanda se torna "Por que eles não escreveram um teste? Seria tão simples!"


0

Como sempre...

Depende

É difícil ver a utilidade desse teste específico em um contexto de TDD, porque não conhecemos a história.

Se a história for como [usuário de mídia], quero [poder verificar a validade da mídia] para que [saiba quando um arquivo não estiver disponível]

o cenário Fornecido [um botão de verificação de mídia] Quando [o usuário clica no botão] Em seguida [a validade do arquivo é verificada]

faz sentido. É trivial, mas faz sentido.

Se a história abrangente for maior que isso, a definição do cenário poderá ser muito estreita.

Lembrar:

TDD! = Teste de Unidade

TDD testa recursos . Se for um recurso, ele merece um teste para verificá-lo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.