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:
- que ele pode retornar raízes simples como sqrt (4.0)
- que ele pode encontrar uma raiz real como sqrt (2.0) com uma precisão razoável
- que ele acha que sqrt (0.0) é 0.0
- 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.01
e 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.