Qual é a melhor maneira de organizar nossos testes de unidade

18

Construímos um número substancial de testes de unidade para o nosso programa principal ao longo dos anos. Vários milhares. O problema é que não temos uma ideia clara de quais testes temos, porque existem muitos. E isso é um problema, porque não sabemos onde somos fracos nos testes (ou onde temos duplicatas).

Nosso aplicativo é um mecanismo de relatórios. Assim, você pode ter um modelo usado para testar a análise (lemos todas as propriedades da tabela), mesclando dados (mantivemos as propriedades corretas da tabela na mesclagem), formatando a página final (a tabela é colocada corretamente na página) ) e / ou o formato de saída (o arquivo DOCX criado está correto).

Adicione a isso o que precisamos testar. Pegue o preenchimento em torno de uma célula da tabela (usamos Word, Excel e PowerPoint para o design do relatório). Temos que testar o preenchimento na quebra de página, para uma tabela dentro de uma célula, células mescladas verticalmente, células mescladas horizontalmente, uma célula mesclada vertical e horizontalmente que contém uma tabela com células mescladas vertical e horizontalmente na tabela interna, onde essa tabela quebra em uma página.

Então, em que categoria esse teste entra? Preenchimento de tabela, quebras de página, células aninhadas, células mescladas verticalmente, células mescladas horizontalmente ou algo mais?

E como documentamos essas categorias, nomeamos os testes de unidade etc.?

Atualização: várias pessoas sugeriram o uso de ferramentas de cobertura para verificar se temos cobertura total. Infelizmente, isso é de uso limitado no nosso caso, porque os bugs tendem a ser devidos a combinações específicas, por isso é o código que foi todo testado, mas não nessa combinação.

Por exemplo, ontem tivemos um cliente que iniciou um marcador do Word no final de um loop forEach em seu modelo (um documento do Word) e o finalizou no início do próximo loop forEach. Todo esse código foi usado com testes de unidade, mas não tínhamos pensado na combinação de um modelo expandindo um início de marcador para ser iniciado 25 vezes e depois terminado 10 vezes (os dois loops forEach tinham um número diferente de linhas).

David Thielen
fonte
1
Parece que sua pergunta é realmente: como sabemos que testamos um cenário específico?
Andy Wiesendanger 04/04
Sim! E também onde estão os testes para quaisquer cenários semelhantes. E a partir disso, obtemos a necessidade nº 2 - ler o que é abordado nos ajuda a encontrar o que perdemos.
David Thielen

Respostas:

13

Geralmente, costumo espelhar a árvore de origem para meus testes de unidade. Portanto, se eu tivesse src / lib / fubar, teria um teste / lib / fubar que conteria os testes de unidade para o fubar.

No entanto, o que você parece estar descrevendo são testes mais funcionais. Nesse caso, eu teria uma tabela multidimensional que enumerasse todas as suas condições possíveis. Então, aqueles que não têm testes não fazem sentido ou precisam de um novo teste. É claro que você pode colocá-los em conjuntos de suítes de teste.

Sardathrion - Restabelecer Monica
fonte
Atualmente, espelhamos a árvore de origem. Mas temos dois problemas. Primeiro, para formatação de tabela, existem mais de 100 testes diferentes. Manter o controle do que exatamente é testado se tornou um problema. Segundo, áreas funcionais muito diferentes precisam testar tabelas - os analisadores, a substituição de dados, a formatação e a criação do documento de saída. Então, acho que você está certo, em certo sentido, é um teste funcional de uma determinada propriedade.
David Thielen
O que leva à pergunta: onde armazenamos a tabela de testes? Estou pensando em uma planilha no diretório raiz de origem ???
David Thielen
Eu o armazenaria em uma planilha controlada por versão no diretório de teste. Se você tem muitas coisas que precisa testar, dividi-las em meta-estruturas seria benéfico. Tente pensar em termos de que coisa geral está sendo testada, em vez de o que ou como.
Sardathrion - Restabelece Monica
7

A estrutura mais comum parece ser o espelho do diretório srce test.

src/module/class
test/module/class_test

No entanto, há uma estrutura alternativa que eu vi que agora acredito ser melhor.

src/module/class
src/module/class_test

Como o teste de unidade tende a espelhar a classe que eles estão testando, colocá-los no mesmo diretório fornece acesso muito mais fácil aos arquivos, para que você possa trabalhar lado a lado.

ming_codes
fonte
2
Uma desvantagem da abordagem anterior é que, toda vez que você decide alterar a estrutura de arquivos do projeto, precisa fazer o mesmo para a estrutura de testes. Esse problema não existe se os testes estiverem onde está o código.
22618 victor175 # 22:02
5

No .NET, costumo espelhar, ou pelo menos aproximar, a estrutura do espaço para nome do código-fonte nos projetos de teste, abaixo do espaço para nome mestre "Tests.Unit" ou "Tests.Integration". Todos os testes de unidade são executados em um projeto, com a estrutura básica do código-fonte replicada como pastas no projeto. O mesmo para testes de integração. Portanto, uma solução simples para um projeto pode ser assim:

Solution
   MyProduct.Project1 (Project)
      Folder1 (Folder)
         ClassAA (Class def)
         ...
      Folder2
         ClassAB
         ...
      ClassAC
      ...
   MyProduct.Project2
      Folder1
         ClassBA
         ...
      ClassBB
      ...
   ...
   MyProduct.Tests.Unit
      Project1
         Folder1
            ClassAATests
            ClassAATests2 (possibly a different fixture setup)
         Folder2
            ClassABTests
         ClassACTests
      Project2
         Folder1
            ClassBATests
         ClassBBTests
      ...
   MyProduct.Tests.Integration
      Project1 (a folder named similarly to the project)
         Folder1 (replicate the folders/namespaces for that project beneath)
            ClassAATests
         Folder2
            ClassABTests
         ClassACTests
      Project2
         Folder1
            ClassBATests
         ClassBBTests

Para quaisquer AATs ou AEETs codificados com uma estrutura de teste de unidade, isso muda um pouco; geralmente esses testes refletem um conjunto de etapas que testam a funcionalidade de um novo caso de uso ou história. Geralmente estruturo esses testes em um MyProduct.Tests.Acceptanceprojeto como tal, com testes para cada história, possivelmente agrupados por marcos ou história "épica" à qual a história em desenvolvimento pertencia. No entanto, esses são realmente apenas testes de integração excessiva e, portanto, se você preferir estruturar os testes de maneira mais orientada a objetos, em vez de orientada a histórias, nem precisa de um MyProduct.Tests.Acceptanceprojeto semelhante; basta jogá-los no MyProduct.Tests.Integrationescopo do objeto de nível mais alto em teste.

KeithS
fonte
3

Não há razão para um teste de unidade estar em apenas uma categoria. Todos os principais conjuntos de ferramentas de teste de unidade apoiar a criação de conjuntos de testes , que empacotam testes para uma determinada categoria. Quando uma área específica do código é alterada, o desenvolvedor deve executar esse conjunto primeiro e frequentemente para ver o que está quebrado. Quando um teste preocupações estofamento e breaks e nidificação, por todos os meios colocá-lo em todos os três suites.

Dito isso, o objetivo dos testes de unidade é executá-los o tempo todo, ou seja, eles devem ser pequenos e rápidos o suficiente para que seja possível executá-los todos antes de cometer qualquer código. Em outras palavras, não importa realmente qual é a categoria de um teste, ele deve ser executado antes de ser confirmado. As suítes são realmente apenas uma muleta que você usa se, por algum motivo, não puder escrever testes tão rápidos quanto deveriam.

Quanto à cobertura, existem ferramentas de cobertura muito boas que informam qual porcentagem de linhas foi realmente executada ao executar seus testes - esse é um indicador óbvio do tipo de teste que você ainda está perdendo.

Quanto à nomenclatura, não há valor particular em gastar esforços nos nomes dos testes de unidade. Tudo o que você deseja ouvir de seus testes é "5235 dos 5235 testes aprovados". Quando um teste falha, o que você lê não é o nome, mas a mensagem , por exemplo, a String no assert()que implementa sua crítica de sucesso. A mensagem deve ser informativa o suficiente para que você tenha uma idéia do que está errado sem ler o corpo do teste. O nome não é importante em comparação com isso.

Kilian Foth
fonte
Concorde 100% com tudo o que você diz (nossa máquina de compilação executa todos os testes no check-in). Nosso grande problema é rastrear o que estamos testando. E a cobertura do código não ajuda muito (veja a atualização acima).
David Thielen
1

Uma maneira de saber se você é fraco nos testes é a rastreabilidade. Geralmente para testes, isso assume a forma de cobertura.

O objetivo é medir quais partes do código são exercidas pelos seus testes, para que você possa ver o código que não é coberto pelos seus testes. Cabe a você (e à ferramenta de cobertura) definir o que é uma "parte do código". O mínimo é a cobertura do ramo.

mouviciel
fonte