Todos os testes de unidade em um executável ou divididos?

12

Ao escrever testes para um software, por exemplo, uma biblioteca, você prefere compilar todos os testes de unidade em um ou separá-los em vários executáveis?

A razão pela qual estou perguntando é porque atualmente estou usando o CUnit para testar uma biblioteca em que estou trabalhando. Os testes são divididos em conjuntos separados, compilados em um executável completo com saída impressa para falhas. Agora, o sistema de compilação para essa biblioteca é o CMake (que, apesar do nome, tem pouco a ver com CUnit), que vem com sua própria estrutura de teste, CTest . O CTest me permite registrar uma lista de executáveis ​​que servem como testes.

Estou pensando em usar o CTest para testes automatizados. No entanto, isso exigiria que eu dividisse os testes que escrevi até agora em destinos de compilação separados. Caso contrário, não posso realmente utilizar alguns dos recursos avançados do CTests, como a execução seletiva de testes.

Sei que isso é mais uma questão de quais ferramentas usar, seu manuseio e convenções, mas, além disso, existem outras razões para preferir um único teste executável do que os separados? Ou vice-versa?

Benjamin Kloster
fonte
Separe-os em executáveis ​​separados por classe. Cada classe deve ter seu próprio teste de unidade, a menos que não seja testável por unidade e, portanto, precise ser testada por outras classes indiretamente.
21712 Brian As
1
Se você tiver uma grande biblioteca com centenas de classes, cada uma com um teste de unidade, o tempo de construção será muito maior se você criar um binário completo para cada uma, comparado a um (ou alguns) grandes binários. Além disso, há muitos makefiles para gerenciar, cada um com linhas de link individuais.
JBRWilkinson
Não é tão grande assim. Menos de 20 módulos. Também posso compilar todos eles com os mesmos sinalizadores, para que o CMake possa gerar os Makefiles sem muito trabalho da minha parte.
Benjamin Kloster

Respostas:

5

Eu gosto de ter meus testes automatizados em binários individuais, ou pelo menos agrupados por grupo "pertence a um grupo", e então os chamo de um script shell simples (onde um código de saída diferente de zero sinaliza falha e a saída no stderr pode ser capturada para gravar uma explicação). Dessa forma, mantenho total flexibilidade nos testes - posso executar testes individuais diretamente da linha de comando, posso criar todo tipo de scripts sofisticados, se quiser, posso reordená-los como achar melhor sem recompilar nada, etc.

Mais importante, porém, também me permite incluir testes escritos em idiomas diferentes ou usar diferentes cadeias de ferramentas na mesma execução. Por exemplo, os testes de unidade que escrevo são provavelmente no idioma principal do projeto, e executá-los é uma questão de criar e chamar os binários; mas também quero testar meu banco de dados e posso alimentar scripts SQL diretamente no banco de dados para isso; Talvez eu queira executar alguma ferramenta de análise de código estática no meu código (mesmo que seja apenas algum tipo de interface). Talvez eu queira executar meu HTML estático por meio de um verificador de validade. Eu poderia executar um grepcomando sobre a base de código para verificar construções suspeitas, violações de estilo de codificação ou palavras-chave "sinalizador vermelho". As possibilidades são infinitas - se puder ser executado a partir da linha de comando e aderir a "status de saída zero significa OK", eu posso usá-lo.

tdammers
fonte
O argumento agnóstico da linguagem é um argumento muito bom, já que planejo implementar ligações python para a biblioteca no futuro. Obrigado!
Benjamin Kloster
@ ddammers: alguma estrutura de teste em particular?
JBRWilkinson
@JBRWilkinson: Apenas um script shell de 30 linhas. Para a maioria dos idiomas que uso, tenho poucas bibliotecas para tarefas de teste comuns, como executar uma função, comparar o resultado com um valor esperado e lançar quando elas não coincidem. Mas qualquer estrutura de teste de unidade pode ser facilmente integrada a esse "sistema meta", desde que possa ser executada a partir da linha de comando e sinalize sucesso / falha através de seu status de saída.
tdammers
2

Costumo ter uma biblioteca para os testes de unidade de um aplicativo (ou para um pacote de bibliotecas que é comumente compartilhado). Dentro dessa biblioteca, tento replicar ou aproximar os namespaces dos objetos em teste para os equipamentos de teste (eu uso o NUnit principalmente). Isso simplifica a compilação, pois no .NET há uma sobrecarga inerente à criação de cada binário que aumentaria o tempo de compilação de uma solução de 20 projetos em comparação com o de uma solução de 10 projetos com o mesmo LOC. Os binários de teste não são distribuídos de qualquer maneira, portanto, qualquer organização dos testes em binários é para sua própria conveniência, e geralmente acho que o YAGNI se aplica aqui como em qualquer lugar.

Agora, eu normalmente não tenho as considerações que o tdammers tem; meu código é praticamente todo em um idioma, e qualquer teste envolvendo cadeias de caracteres SQL não é um teste de unidade (a menos que você esteja testando se um produtor de consultas retorna a cadeia SQL esperada com base em certos critérios) e eu praticamente nunca testo as unidades reais UI (em muitas situações, é simplesmente impossível). Também uso uma biblioteca de teste de unidade que é bem aceita por ferramentas de terceiros, como bots de compilação e plugins IDE, e, portanto, qualquer preocupação em executar testes individuais, suítes parciais etc. é mínima.

KeithS
fonte
1
Uma questão de cultura, eu acho - em um ambiente .NET, eu provavelmente raciocinaria como você.
tdammers