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


47
  • Esta pergunta não é sobre estruturas de teste de unidade.
  • Esta pergunta não é sobre escrever testes de unidade.
  • Esta pergunta é sobre onde colocar o código UT escrito e como / quando / onde compilar e executá-lo.

Ao trabalhar efetivamente com o código legado , Michael Feathers afirma que

bons testes de unidade ... corra rápido

e essa

Um teste de unidade que leva 1/10 de segundo para executar é um teste de unidade lento.

Eu acho que essas definições fazem sentido. Também acho que eles implicam que você deve manter um conjunto de testes de unidade e um conjunto desses testes de código que demoram mais tempo separadamente, mas acho que esse é o preço que você paga apenas por chamar algo de teste de unidade se for executado (muito) rapidamente .

Obviamente, o problema em C ++ é que a "correr" o teste de unidade ( s ), você tem que:

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

Editar (após uma votação estranha) : Antes de entrar em detalhes, tentarei resumir o ponto aqui:

Como o código de teste de unidade C ++ pode ser organizado de maneira eficaz, para que seja eficiente editar o código (teste) e executá-lo?


O primeiro problema é 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 da unidade que você está mudando no momento

O segundo problema, relacionado, é o que compilar para que o feedback seja instantâneo.

Opções extremas:

  • Cada unidade-teste-teste-unidade vive em um arquivo cpp separado e esse arquivo cpp é compilado + vinculado separadamente (junto com o arquivo de unidade de código-fonte que ele testa) a um único executável que executa esse teste de unidade.
    • (+) Isso minimiza o tempo de inicialização (compilação + link!) Para a única unidade de teste.
    • (+) O teste é executado super rápido, porque apenas testa uma unidade.
    • (-) A execução de todo o conjunto precisará iniciar um bazilhão de processos. Pode ser um problema para gerenciar.
    • (-) A sobrecarga do início do processo se tornará 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 testam!) Estão vinculados a um executável (por módulo / por projeto / escolha sua opção).
    • (+) O tempo de compilação ainda seria bom, pois apenas o código alterado será compilado.
    • (+) A execução de todo o conjunto é fácil, pois existe apenas um exe para executar.
    • (-) O conjunto levará séculos para ser vinculado, pois cada recompilação de qualquer objeto acionará um re-link.
    • (-) (?) O processo levará mais tempo para ser executado, embora se todos os testes de unidade forem rápidos, o tempo deve ser bom.

Então, como são tratados os testes de unidade C ++ do mundo real ? Se eu executar essas coisas todas as noites / a cada hora, a segunda parte realmente não importa, mas a primeira parte, a saber, como "acoplar" o código UT ao código de produção, para que seja "natural" que os desenvolvedores mantenham os dois em foco sempre importa, eu acho. (E se os desenvolvedores tiverem o código UT em foco, eles quererão executá-lo, o que nos leva de volta à parte dois.)

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

Notas:

  • Esta pergunta deixa intencionalmente plataforma não especificada e sistema de criação / projeto.
  • Perguntas com a tag UT & C ++ é um ótimo ponto de partida, mas infelizmente muitas perguntas e especialmente respostas estão muito focadas nos detalhes ou em estruturas específicas.
  • Há um tempo atrás, eu respondi uma pergunta semelhante sobre estrutura para testes de unidade de impulso. Acho que falta essa estrutura para testes de unidade rápidos e "reais". E acho a outra pergunta muito estreita, daí a nova pergunta.

6
Uma complicação adicional é introduzida pelo idioma C ++ de mover o maior número possível de erros para compilar o tempo - um bom conjunto de testes de unidade geralmente precisa ser capaz de testar se determinado uso falha na compilação.

3
@ Closers: Você poderia fornecer os argumentos que o levaram a encerrar esta pergunta?
Luc Touraille

Não vejo bem por que isso tinha que ser fechado. :-(Onde você deve procurar respostas para essas perguntas, se não estiver neste fórum?

2
@ Joe: Por que preciso de um teste de unidade para descobrir se algo será compilado quando o compilador me disser isso?
David Thornley

2
@ David: Porque você quer ter certeza de que não compila . Um exemplo rápido seria um objeto de pipeline que aceita uma entrada e produz saída Pipeline<A,B>.connect(Pipeline<B,C>)deve compilar enquanto Pipeline<A,B>.connect(Pipeline<C,D>)não deve compilar: O tipo de saída do primeiro estágio é incompatível com o tipo de entrada do segundo estágio.
sebastiangeiger

Respostas:


6

Temos todos os testes de unidade (para um módulo) em um executável. Os testes são colocados em grupos. 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 testes. O sistema de build 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.


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, não saberá qual causou.

Eu gosto de colocar testes de unidade em uma árvore de diretórios que sombreie a árvore principal. Se eu tenho /sources/componentA/alpha/foo.cce /objects/componentA/beta/foo.o, então eu quero algo como /UTest_sources/componentA/alpha/test_foo.cce /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 extremos, mas esse esquema simplifica bastante as coisas. Uma boa macro de editor pode acessar a fonte de teste ao lado da fonte do assunto sem esforço. Um bom sistema de compilação (por exemplo, GNUMake) pode compilar os dois e executar o teste com um comando (por exemplo make test_foo), e pode gerenciar um bilhão de processos - apenas aqueles cujas fontes foram alteradas desde a última vez que testaram - com bastante facilidade (eu nunca considerou a sobrecarga de iniciar esses processos um problema, é O (N)).

Na mesma estrutura, você pode ter testes em grande escala (não mais testes de unidade) que vinculam muitos objetos e executam muitos testes. O truque é classificar esses testes pelo tempo que eles levam para criar / executar e trabalhá-los em sua programação diária de acordo. Execute o teste de um segundo ou menos sempre que lhe apetecer; iniciar o teste de dez segundos e esticar; teste de cinco minutos e faça uma pausa; teste de meia hora e vá almoçar; teste de seis horas e vá para casa. Se você acha que está perdendo muito tempo, por exemplo, revinculando um teste enorme depois de alterar apenas um arquivo pequeno, está fazendo algo errado - mesmo que a ligação fosse instantânea, você ainda estaria executando um teste longo quando não foi chamado.


ad (1) - sim, que foi formulada de forma descuidada
Martin Ba
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.