Se você tiver apenas uma afirmação por teste; como testar várias entradas?

15

Estou tentando criar alguns casos de teste e li que você deve tentar limitar o número de asserções por caso de teste.

Então, minha pergunta é: qual é a melhor maneira de testar uma função com várias entradas. Por exemplo, eu tenho uma função que analisa uma string do usuário e retorna o número de minutos. A cadeia pode estar no formato "5w6h2d1m", onde w, h, d, mcorresponde ao número de semanas, horas, dias e minutos.

Se eu quisesse seguir a 'regra de 1 afirmação por teste', teria que fazer vários testes para cada variação de entrada? Isso parece bobagem, então eu só tenho algo como:

self.assertEqual(parse_date('5m'), 5)
self.assertEqual(parse_date('5h'), 300)
self.assertEqual(parse_date('5d') ,7200)
self.assertEqual(parse_date('1d4h20m'), 1700)

No caso de um teste. Existe uma maneira melhor?

espancar
fonte
A melhor maneira de fazer isso é usar parâmetros (algumas estruturas suportam esse recurso, todas as estruturas devem). Desta forma, você está testando um único comportamento, mas tendo em conta muitos casos de teste e ainda pode ver o que os valores dos parâmetros causou um erro se ocorrer um erro
Kemoda

Respostas:

23

Uma maneira mais pragmática de visualizar a "regra" de uma afirmação por teste é fazer com que suas afirmações em um único teste abranjam um único conceito.

Dessa forma, você ainda está testando um único conceito em um único teste. No seu caso, se sua sequência de entrada é analisada corretamente em uma única data.

Você deve exercer seu julgamento caso a caso para verificar se é melhor testar um único conceito com múltiplas afirmações ou ter uma única afirmação em muitos testes.

Escolha a opção que permite testes mais claros, menos repetições, enquanto os testes continuam sendo capazes de destacar diferentes pontos de falhas em seu método. Você deseja que fique claro quando um teste falhar exatamente o que aconteceu, em vez de precisar depurá-lo para descobrir o que deu errado.

Gilles
fonte
17

Depende muito da sua biblioteca de testes. Na biblioteca C # NUnit, você pode fazer algo como:

[TestCase('5m', 5)]
[TestCase('5h', 300)]
[TestCase('5d', 7200)]
[TestCase('1d4h20m', 1700)]
public void ParseDateTest(inputString, expectedMinutes)
{
    Assert.That(parse_date(inputString), Is.EqualTo(expectedMinutes));
}
Scroog1
fonte
Em java com testng você tem métodos @DataProvider
Kemoda
essa é a melhor solução IMHO. em quase todos os idiomas, você pode parametrizar seus testes. para java: @Parameterized , JunitParams , Zohhak
piotrek
3

Sim, faça vários testes para cada variação de entrada.

O principal objetivo da afirmação por orientação de teste é ter (idealmente) que um erro leve a uma falha no teste e vice-versa, para que você saiba exatamente o que falhou. Em seguida, você pode trabalhar com um teste muito preciso para depurar a causa raiz e verificar. Você pode quebrar isso com uma afirmação e pode aceitar várias afirmações. Nesse cenário específico, eu teria um teste para cada sufixo e algumas combinações de pedidos.

Espero que esteja claro por que o isolamento de testes é um benefício: você perde menos tempo depurando quando algo dá errado. Agora, se você realmente tem certeza de que é improvável que o teste falhe, e que a sobrecarga da busca pelo teste é pequena, talvez faça sentido testá-los todos de uma vez para economizar tempo de implementação.

Mas a história mostrou que economizar um pouco de tempo escrevendo código às custas da leitura / utilização do código nunca vale a pena. Daí a orientação.

Telastyn
fonte
1

Os puristas diriam que asserções para diferentes valores de entrada devem ser colocadas em métodos de teste separados dentro da classe de teste. Uma razão para isso é que, dependendo da interface do usuário de teste, muitas vezes é mais fácil distinguir entre falhas de teste individuais do que entre afirmações individuais, o que pode levar você a identificar a fonte da falha mais rapidamente.

Ao testar com JUnit, podemos contornar isso usando a versão dos métodos assert * com um argumento String inicial para diferenciar uma asserção de outra dentro do mesmo método de teste.

self.assertEqual("just minutes", parse_date('5m'), 5)
self.assertEqual("just hours", parse_date('5h'), 300)
self.assertEqual("just days", ('5d') ,7200)
self.assertEqual("days, hours, minutes", parse_date('1d4h20m'), 1700)
Mike Partridge
fonte