Ao pesquisar as práticas recomendadas de teste de unidade para ajudar a elaborar diretrizes para minha organização, eu me deparei com a questão de saber se é melhor ou útil separar equipamentos de teste (classes de teste) ou manter todos os testes para uma única classe em um arquivo.
Fwiw, estou me referindo a "testes de unidade" no sentido puro de que são testes de caixa branca direcionados a uma única classe, uma asserção por teste, todas as dependências zombadas etc.
Um cenário de exemplo é uma classe (chamada Documento) que possui dois métodos: CheckIn e CheckOut. Cada método implementa várias regras, etc., que controlam seu comportamento. Seguindo a regra de uma asserção por teste, terei vários testes para cada método. Posso colocar todos os testes em uma única DocumentTests
classe com nomes como CheckInShouldThrowExceptionWhenUserIsUnauthorized
e CheckOutShouldThrowExceptionWhenUserIsUnauthorized
.
Ou, eu poderia ter duas classes de teste separadas: CheckInShould
e CheckOutShould
. Nesse caso, meus nomes de teste seriam reduzidos, mas eles seriam organizados para que todos os testes de um comportamento específico (método) estejam juntos.
Tenho certeza de que existem prós e contras em qualquer uma das abordagens e estou imaginando se alguém já seguiu o caminho com vários arquivos e, se sim, por quê? Ou, se você optou pela abordagem de arquivo único, por que acha que é melhor?
fonte
testResponseContainsSuccessTrue()
,testResponseContainsMyData()
etestResponseStatusCodeIsOk()
. Você poderia tê-los em um únicotestResponse()
que tem três afirma:assertEquals(200, response.status)
,assertEquals({"data": "mydata"}, response.data)
eassertEquals(true, response.success)
Respostas:
É raro, mas às vezes faz sentido ter várias classes de teste para uma determinada classe em teste. Normalmente, eu faria isso quando diferentes configurações são necessárias e compartilhadas em um subconjunto dos testes.
fonte
Não vejo realmente nenhuma razão convincente para dividir um teste para uma única classe em várias classes de teste. Como a idéia principal deve ser manter a coesão no nível de classe, você deve se esforçar para isso também no nível de teste. Apenas algumas razões aleatórias:
fonte
Se você for obrigado a dividir testes de unidade para uma classe em vários arquivos, isso pode ser uma indicação de que a própria classe é mal projetada. Não consigo pensar em nenhum cenário em que seria mais benéfico dividir os testes de unidade para uma classe que cumpra razoavelmente o Princípio de Responsabilidade Única e outras práticas recomendadas de programação.
Além disso, ter nomes de métodos mais longos em testes de unidade é aceitável, mas se isso o incomoda, você sempre pode repensar sua convenção de nomenclatura para reduzir os nomes.
fonte
Um dos meus argumentos contra a separação dos testes em várias classes é que fica mais difícil para outros desenvolvedores da equipe (especialmente aqueles que não são tão conhecedores de testes) localizar os testes existentes ( Puxa, gostaria de saber se já existe um teste para isso). Gostaria de saber onde seria? ) e também onde colocar novos testes ( vou escrever um teste para esse método nesta classe, mas não tenho muita certeza se devo colocá-los em um novo arquivo ou em um existente um? )
No caso em que testes diferentes exigem configurações drasticamente diferentes, vi alguns profissionais do TDD colocarem o "acessório" ou o "setup" em diferentes classes / arquivos, mas não os próprios testes.
fonte
Gostaria de poder lembrar / encontrar o link em que a técnica que escolhi adotar foi demonstrada pela primeira vez. Essencialmente, eu crio uma classe única e abstrata para cada classe em teste que contém equipamentos de teste aninhados (classes) para cada membro que está sendo testado. Isso fornece a separação desejada originalmente, mas mantém todos os testes no mesmo arquivo para cada local. Além disso, isso gera um nome de classe que permite fácil agrupamento e classificação no executor de teste.
Aqui está um exemplo de como essa abordagem é aplicada ao meu cenário original:
Isso resulta nos seguintes testes que aparecem na lista de testes:
À medida que mais testes são adicionados, eles são facilmente agrupados e classificados por nome de classe, o que também mantém todos os testes da classe em teste listados juntos.
Outra vantagem dessa abordagem que aprendi a aproveitar é a natureza orientada a objetos dessa estrutura significa que posso definir o código dentro dos métodos de inicialização e limpeza da classe base DocumentTests que serão compartilhados por todas as classes aninhadas e dentro de cada uma. classe aninhada para os testes que ela contém.
fonte
Tente pensar o contrário por um minuto.
Por que você teria apenas uma classe de teste por classe? Você está fazendo um teste de classe ou de unidade? Você ainda depende de aulas ?
Um teste de unidade deve estar testando um comportamento específico, em um contexto específico. O fato de sua classe já ter um
CheckIn
meio deve ter um comportamento que precisa dele em primeiro lugar.Como você pensaria sobre esse pseudocódigo:
Agora você não está testando diretamente o
checkIn
método. Em vez disso, você está testando um comportamento (que é fazer check-in felizmente;)) e, se precisar refatoráDocument
-lo e dividi-lo em classes diferentes, ou mesclar outra classe, seus arquivos de teste ainda são coerentes, ainda fazem sentido, pois você nunca muda a lógica ao refatorar, apenas a estrutura.Um arquivo de teste para uma classe simplesmente torna mais difícil refatorar sempre que você precisar, e os testes também fazem menos sentido em termos de domínio / lógica do próprio código.
fonte
Em geral, eu recomendaria pensar em testes como testar um comportamento da classe em vez de um método. Ou seja, alguns de seus testes podem precisar chamar os dois métodos na classe para testar o comportamento esperado.
Geralmente começo com uma classe de teste de unidade por classe de produção, mas pode eventualmente dividir essa classe de teste em várias classes de teste com base no comportamento que eles testam. Em outras palavras, eu recomendaria não dividir a classe de teste em CheckInShould e CheckOutShould, mas sim dividir pelo comportamento da unidade em teste.
fonte
1. Considere o que provavelmente dará errado
Durante o desenvolvimento inicial, você sabe muito bem o que está fazendo e as duas soluções provavelmente funcionarão bem.
Fica mais interessante quando um teste falha após uma alteração muito mais tarde. Existem duas possibilidades:
CheckIn
(ouCheckOut
). Novamente, tanto a solução de arquivo único quanto a solução de dois arquivos estão corretas nesse caso.CheckIn
eCheckOut
(e talvez os testes) de uma maneira que faça sentido para cada um deles, mas não para os dois juntos. Você quebrou a coerência do par. Nesse caso, dividir os testes em dois arquivos dificultará a compreensão do problema.2. Considere para que testes são usados
Os testes têm dois propósitos principais:
Então?
Ambas as perspectivas sugerem que manter os testes juntos pode ajudar, mas não vai doer.
fonte