Como você escreve casos de teste de unidade?

14

Às vezes, acabo escrevendo casos de teste de unidade para o código que outros desenvolvedores escreveram. Há ocasiões em que realmente não sei o que o desenvolvedor está tentando fazer (a parte comercial) e apenas manipulo o caso de teste para obter a linha verde. Essas coisas são normais no setor?

Qual é a tendência normal? Os desenvolvedores devem escrever casos de teste de unidade para o código que eles mesmos escreveram?

Vinoth Kumar CM
fonte
2
"força"? O que significa "dint"?
S.Lott 25/02

Respostas:

12

Tente ler este post do blog: Escrevendo ótimos testes de unidade: melhores e piores práticas .

Mas existem inúmeros outros na web.

Em resposta direta às suas perguntas ...

  1. "Tendência normal" - acho que isso pode diferir de um lugar para outro, o que é normal para mim pode ser estranho para os outros.
  2. Eu diria (na minha opção) que o desenvolvedor que escreve o código deve escrever o teste, idealmente usando métodos como TDD, onde você escreveria o teste antes do código. Mas outros podem ter métodos e idéias diferentes aqui!

E a maneira como você descreveu ao escrever os testes (na sua pergunta) está totalmente errada!


fonte
9

Essa abordagem torna o teste de unidade inútil.

É necessário que o teste de unidade falhe quando alguma ação real não funcionar como pretendido. Se você não fizer isso dessa maneira, e talvez até escrever o teste antes do código para testar, é como ter alarmes de fumaça que não funcionem.


fonte
8
Isso não é bem verdade. Ou melhor, é verdade em um mundo ideal, mas, infelizmente, muitas vezes estamos longe disso. Considere ter código legado sem testes e sem especificações, e sem alguém que possa lhe fornecer detalhes minuciosos com precisão, o que uma parte específica do código deve fazer com precisão (isso é realidade em grande parte dos projetos existentes). Mesmo nesse caso, ainda vale a pena escrever testes de unidade para bloquear o estado atual do código e garantir que você não interrompa nada com refatoração futura, correções de bugs ou extensões.
Péter Török
2
Além disso, acho que você quis dizer "escreva o teste após o código para testar", não é?
Péter Török
@ Péter, o texto deu errado - você acertou. Mas, se você decidir escrever testes, eles devem fazer algo para ser útil. Invocar cegamente o código dizendo que é um teste não é - na minha opinião - um teste.
Ora, se você quer dizer que devemos ter afirmações significativas em nossos testes de unidade, para verificar se o código testado realmente faz o que pensamos , concordo plenamente.
Péter Török
3

Se você não sabe o que uma função faz, não pode escrever um teste de unidade para ela. Pelo que você sabe, nem faz o que deveria. Você precisa descobrir o que deve fazer primeiro. ENTÃO escreva o teste.

Edward Strange
fonte
3

No mundo real, é perfeitamente normal escrever testes de unidade para o código de outra pessoa. Claro, o desenvolvedor original já deveria ter feito isso, mas muitas vezes você recebe código legado onde isso simplesmente não foi feito. A propósito, não importa se esse código legado veio décadas atrás de uma galáxia muito, muito distante, ou se um de seus colegas de trabalho o verificou na semana passada ou se você o escreveu hoje, o código legado é código sem testes

Pergunte a si mesmo: por que escrevemos testes de unidade? Tornar-se verde é obviamente apenas um meio para atingir um fim, o objetivo final é provar ou refutar afirmações sobre o código em teste.

Digamos que você tenha um método que calcule a raiz quadrada de um número de ponto flutuante. Em Java, a interface o definiria como:

public double squareRoot(double number);

Não importa se você escreveu a implementação ou se alguém o fez, você deseja afirmar algumas propriedades do squareRoot:

  1. que ele pode retornar raízes simples como sqrt (4.0)
  2. que ele pode encontrar uma raiz real como sqrt (2.0) com uma precisão razoável
  3. que ele acha que sqrt (0.0) é 0.0
  4. que lança uma IllegalArgumentException quando alimentado um número negativo, ou seja, no sqrt (-1.0)

Então você começa a escrever isso como testes individuais:

@Test
public void canFindSimpleRoot() {
  assertEquals(2, squareRoot(4), epsilon);
}

Ops, este teste já falha:

java.lang.AssertionError: Use assertEquals(expected, actual, delta) to compare floating-point numbers

Você se esqueceu da aritmética de ponto flutuante. OK, você apresenta double epsilon=0.01e vai:

@Test
public void canFindSimpleRootToEpsilonPrecision() {
  assertEquals(2, squareRoot(4), epsilon);
}

e adicione os outros testes: finalmente

@Test
@ExpectedException(IllegalArgumentException.class)
public void throwsExceptionOnNegativeInput() {
  assertEquals(-1, squareRoot(-1), epsilon);
}

e opa, novamente:

java.lang.AssertionError: expected:<-1.0> but was:<NaN>

Você deveria ter testado:

@Test
public void returnsNaNOnNegativeInput() {
  assertEquals(Double.NaN, squareRoot(-1), epsilon);
}

O que fizemos aqui? Começamos com algumas suposições sobre como o método deveria se comportar e descobrimos que nem todas eram verdadeiras. Em seguida, fizemos o conjunto de testes Green, para anotar a prova de que o método se comporta de acordo com nossas suposições corrigidas. Agora, os clientes desse código podem confiar nesse comportamento. Se alguém trocasse a implementação real do squareRoot por outra coisa, algo que, por exemplo, realmente causou uma exceção em vez de retornar NaN, nossos testes perceberiam isso imediatamente.

Este exemplo é trivial, mas geralmente você herda grandes partes de código, onde não está claro o que realmente faz. Nesse caso, é normal estabelecer um equipamento de teste em torno do código. Comece com algumas suposições básicas sobre como o código deve se comportar, escreva testes de unidade para eles, teste. Se Verde, bom, escreva mais testes. Se vermelho, agora você tem uma afirmação falhada que pode ser mantida contra uma especificação. Talvez haja um erro no código legado. Talvez a especificação não esteja clara sobre essa entrada específica. Talvez você não tenha uma especificação. Nesse caso, reescreva o teste para documentar o comportamento inesperado:

@Test
public void throwsNoExceptionOnNegativeInput() {
  assertNotNull(squareRoot(-1)); // Shouldn't this fail?
}

Com o tempo, você acaba com um equipamento de teste que documenta como o código realmente se comporta e se torna uma espécie de especificação codificada. Se você quiser alterar o código legado ou substituí-lo por outra coisa, você tem o recurso de teste para verificar se o novo código se comporta da mesma forma ou se o novo código se comporta de maneira diferente nas formas esperadas e controladas (por exemplo, na verdade (corrige o erro que você espera que seja corrigido). Esse arnês não precisa estar completo no primeiro dia; de fato, ter um arnês incompleto é quase sempre melhor do que não ter arnês. Ter um arnês significa que você pode escrever o código do seu cliente com mais facilidade, sabe onde esperar que as coisas quebrem quando você muda alguma coisa e onde elas quebraram quando acabaram.

Você deve tentar sair da mentalidade de que precisa escrever testes de unidade apenas porque precisa, como se fosse preencher campos obrigatórios em um formulário. E você não deve escrever testes de unidade apenas para tornar a linha vermelha verde. Testes de unidade não são seus inimigos, testes de unidade são seus amigos.

wallenborn
fonte
1

Quando estou escrevendo casos de teste (para impressoras), tento pensar em cada pequeno componente .... e no que posso fazer para quebrá-lo. Então, digamos que o scanner, por exemplo, quais comandos ele usa (na linguagem de trabalho da impressora pjl) o que eu posso escrever para testar todas as funcionalidades ... Ok, agora o que posso fazer para tentar quebrar isso.

Eu tento fazer isso para cada componente principal, mas quando se trata de software e não de muito hardware, você deseja examinar cada método / função e verificar os limites e outros.


fonte
1

Parece que você está trabalhando com outros desenvolvedores (ou mantendo o código gravado por outros desenvolvedores) que não fazem testes de unidade. Nesse caso, acho que você definitivamente gostaria de saber o que o objeto ou método que você está testando deve fazer e criar um teste para ele.

Não será TDD porque você não escreveu o teste primeiro, mas pode melhorar a situação. Você também pode criar uma cópia dos objetos em teste com stubs, para poder estabelecer que seus testes funcionem corretamente quando o código falhar.

vjones
fonte