Como o código de Teste de Unidade C ++ deve ser organizado para a eficiência máxima do Teste de Unidade?

45
  • Esta questão não é sobre as estruturas de teste de unidades.
  • Esta questão não é sobre como escrever Testes Unitários.
  • Esta questão é sobre onde colocar o código UT escrito e como / quando / onde compilá-lo e executá-lo.

Em Trabalhando efetivamente com o Código Legado , Michael Feathers afirma que

good unit tests ... run fast

e que

A unit test that takes 1/10th of a second to run is a slow unit test.

Eu acho que essas definições fazem sentido. Eu também acho que eles implicam que você tem que manter um conjunto de Testes de Unidade e um conjunto de Os Testes de Código que Demoram Mais separadamente, mas eu acho que é o preço que você paga por apenas chamar algo de um Teste de Unidade se ele for executado (muito) rápido.

Obviamente, o problema em C ++ é que para "executar" seu Teste de Unidade ( s ), você precisa:

  1. Edite seu código (produção ou teste de unidade, dependendo de qual "ciclo" você está)
  2. Compilar
  3. Link
  4. Iniciar o executável de teste de unidade ( s )

Editar (depois de uma votação estranha) : Antes de entrar nos detalhes, vou tentar resumir o ponto aqui:

Como o código de Teste de Unidade do C ++ pode ser organizado de forma eficaz, de modo que seja eficiente editar o código (teste) e executar o código de teste?

O problema primeiro é então decidir onde colocar o código do Teste de Unidade para que:

  • é "natural" editá-lo e visualizá-lo em combinação com o código de produção associado.
  • é fácil / rápido iniciar o ciclo de compilação para a unidade que está sendo alterada no momento

O segundo problema relacionado, então, é o que é compilado para que o feedback seja instantâneo.

Opções extremas:

  • Cada Unit-Test-Test-Unit vive em um arquivo cpp separado e esse arquivo cpp é compilado + vinculado separadamente (junto com o arquivo de unidade de código-fonte testado) para um único executável que executa este Teste Unitário.
    • (+) Isso minimiza o tempo de inicialização (compilação + link!) para a única Unidade de teste.
    • (+) O teste é executado super rápido, porque ele testa apenas uma unidade.
    • (-) A execução de todo o conjunto precisará iniciar um bazillion de processos. Pode ser um problema para gerenciar.
    • (-) A sobrecarga de processos começa a ficar visível
  • O outro lado seria ter - ainda - um arquivo cpp por teste, mas todos os arquivos cpp de teste (junto com o código que eles testam!) são vinculados em um executável (por módulo / por projeto / escolha seu escolha).
    • (+) O tempo de compilação ainda seria OK, pois somente o código alterado será compilado.
    • (+) A execução de todo o pacote é fácil, pois há apenas um exe para ser executado.
    • (-) A suíte levará tempo para vincular, pois cada recompilação de qualquer objeto acionará um novo link.
    • (-) (?) O processo levará mais tempo para ser executado, embora se todos os Testes de Unidade forem rápidos, o tempo deve ser OK.

Então, como são tratados os Testes de Unidade de C ++ do mundo real? Se eu só executo esse material todas as noites / horas, a segunda parte realmente não importa, mas a primeira parte, como "acoplar" o código UT ao código de produção, para que seja "natural" que os desenvolvedores mantenham ambos em foco sempre importa, eu acho. (E se os desenvolvedores tiverem o código UT em foco, eles vão querer executá-lo, o que nos traz de volta à segunda parte.)

Histórias do mundo real e experiência apreciada!

Notas:

  • Esta questão deixa intencionalmente a plataforma não especificada e o sistema make / project.
  • Perguntas marcadas como UT & C ++ é um ótimo lugar para começar, mas infelizmente muitas perguntas, e especialmente as respostas, estão muito concentradas nos detalhes ou em estruturas específicas.
  • Há algum tempo, eu respondi a um pergunta semelhante na estrutura para testes unitários de reforço. Eu acho que esta estrutura está faltando para testes unitários "reais" e rápidos. E eu acho a outra questão muito estreita, daí esta nova pergunta.
por Martin Ba 31.08.2011 / 13:25
fonte

2 respostas

6

Temos todos os testes unitários (para um módulo) em um executável. Os testes são colocados em grupos. Eu posso executar um único teste (ou alguns testes) ou um grupo de testes, especificando um nome (teste / grupo) na linha de comando do executor de teste. O sistema de compilação pode executar o grupo "Build", o departamento de teste pode executar "All". O desenvolvedor pode colocar alguns testes em um grupo como "BUG1234", com 1234 sendo o número do rastreador de problemas do caso em que ele está trabalhando.

    
por 31.08.2011 / 13:38
fonte
6

Primeiro, eu discordo de "1) Edite seu código (de produção) e seu Teste de Unidade". Você deve modificar apenas um de cada vez, caso contrário, se o resultado mudar, você não saberá o que causou isso.

Eu gosto de colocar testes de unidade em uma árvore de diretórios que sombreia a árvore principal. Se eu tiver /sources/componentA/alpha/foo.cc e /objects/componentA/beta/foo.o , quero algo como /UTest_sources/componentA/alpha/test_foo.cc e /UTest_objects/componentA/beta/test_foo.o . Eu uso a mesma árvore de sombra para objetos stub / mock e quaisquer outras fontes que os testes precisem. Haverá alguns casos de borda, mas esse esquema simplifica muito as coisas. Um bom editor macro pode puxar a fonte de teste ao lado da fonte de assunto sem esforço. Um bom sistema de compilação (por exemplo, GNUMake) pode compilar ambos e executar o teste com um comando (por exemplo, make test_foo ) e pode gerenciar um bazillion desses processos - apenas aqueles cujas origens foram alteradas desde a última vez em que foram testados - facilmente (Eu nunca encontrei a sobrecarga de iniciar esses processos para ser um problema, é O (N)).

No mesmo framework, você pode ter testes em larga escala (não mais testes de unidade) que ligam muitos objetos juntos e executam muitos testes. O truque é classificar esses testes por quanto tempo eles levam para construir / executar e trabalhá-los em sua programação diária de acordo. Execute o teste de um segundo ou menos sempre que desejar; iniciar o teste de dez segundos e alongar; teste de cinco minutos e faça uma pausa; teste de meia hora e ir para o almoço; teste de seis horas e ir para casa. Se você achar que está perdendo muito tempo, por exemplo revivendo um grande teste depois de alterar apenas um arquivo pequeno, você está fazendo tudo errado - mesmo se a vinculação fosse instantânea, você ainda estaria realizando um longo teste quando não fosse necessário.

    
por 31.08.2011 / 18:43
fonte