Portanto, estou tentando fazer meus testes de unidade o mais simples possível, mas isso se torna problemático quando estou testando alguns métodos simples de Adicionar / Excluir.
Para o método add, eu basicamente tenho que criar um objeto fictício e adicioná-lo; depois que o teste for bem-sucedido, eu tenho que excluir o objeto fictício.
E para o teste de exclusão, obviamente tenho que criar um objeto fictício para poder excluí-lo.
Como você pode ver se um teste falha, o outro também falha, pois ambos são necessários.
O mesmo acontece com um sistema em que você precisaria escrever um teste que "Cancela uma ordem" ... bem, seria necessária uma ordem fictícia para cancelar primeiro, isso não é contrário às diretrizes do teste de unidade?
Como casos como esse devem ser tratados?
fonte
Respostas:
Bem, não há nada errado com o que você está fazendo. Vários testes podem cobrir o mesmo código; significa apenas que um problema fará com que vários testes falhem. O que você deseja evitar são testes que dependem dos resultados de outros testes. Ou seja, seu teste de exclusão depende da execução do teste de adição e, portanto, se você executar o teste de exclusão ANTES de adicionar, ele falhará. Para evitar esse problema, verifique se você tem uma "folha em branco" no início de cada teste, para que o que acontece em um determinado teste não possa afetar os testes subsequentes.
Uma ótima maneira de fazer isso é executar os testes em um banco de dados na memória.
No seu teste de adição, crie um banco de dados vazio, adicione o objeto e afirme que ele realmente foi adicionado.
No seu teste de exclusão, crie o banco de dados com o objeto que você já estará excluindo. Exclua o objeto e afirme que ele foi excluído.
Sopre o banco de dados em seu código desmontável.
fonte
Use transações.
Se você estiver usando um banco de dados que suporta transações, execute cada teste em uma transação. Retroceda a transação no final do teste. Cada teste deixará o banco de dados inalterado.
Sim, para cancelar um pedido, você primeiro precisará criar um. Isso é bom. O teste primeiro cria um pedido, depois o cancela e verifica se o pedido foi cancelado.
fonte
Você está indo bem. O primeiro e único princípio fundamental do teste de unidade é cobrir todos os caminhos de código que você possui, para que você possa ter certeza de que seu código está fazendo o que deveria e continua fazendo isso depois de alterações e refatorações. Manter os testes de unidade pequenos, simples e de uso único é uma meta que vale a pena, mas não é fundamental. Ter um teste que chame dois métodos de relacionar sua API não é, por si só, questionável; na verdade, como você ressalta, geralmente é necessário. A desvantagem de ter testes redundantes é que eles levam mais tempo para escrever, mas como quase tudo em desenvolvimento, essa é uma troca que você precisa fazer o tempo todo, e a melhor solução quase nunca é um dos pontos extremos.
fonte
As técnicas de teste de software são extremamente variadas e, quanto mais você se instruir sobre elas, começará a ver muitas orientações diferentes (e às vezes conflitantes). Não há um único livro para seguir.
Eu acho que você está em uma situação em que você viu algumas orientações para testes de unidade que dizem coisas como
e assim por diante. E tudo isso está certo, dependendo de como você define 'teste de unidade' .
Eu definiria um 'teste de unidade' como algo como: "um teste que exercita uma parte da funcionalidade de uma unidade de código, isolada de outros componentes dependentes".
Sob essa definição, o que você está fazendo (se precisar adicionar um registro a um banco de dados antes de poder executar o teste) não é um 'teste de unidade', mas mais do que é comumente chamado de 'teste de integração'. (Pela minha definição, um verdadeiro teste de unidade não atingirá o banco de dados; portanto, você não precisará adicionar um registro antes de excluí-lo.)
Um teste de integração exercitará a funcionalidade que usa vários componentes (como uma interface com o usuário e um banco de dados), e as orientações que se aplicariam aos testes de unidade não se aplicam necessariamente aos testes de integração.
Como outras pessoas mencionaram em suas respostas, o que você está fazendo não é necessariamente errado, mesmo que você faça coisas contrárias a algumas orientações de teste de unidade. Em vez disso, tente argumentar sobre o que você está realmente testando em cada método de teste e, se achar que precisa de vários componentes para satisfazer seu teste, e alguns componentes exigem pré-configuração, vá em frente e faça-o.
Mas, acima de tudo, entenda que existem muitos tipos de testes de software (testes de unidade, testes de sistema, testes de integração, testes exploratórios etc.) e não tente aplicar a orientação de um tipo a todos os outros.
fonte
É exatamente por isso que uma das outras diretrizes é usar interfaces. Se você usar um objeto que implementa uma interface em vez de uma implementação de classe específica, você pode criar uma classe que não dependa do restante da base de código.
Outra alternativa é usar uma estrutura de simulação. Isso permite que você crie facilmente esses tipos de objetos fictícios que podem ser passados para o método que você está testando. É possível que você precise criar algumas implementações de stub para a classe dummy, mas ele ainda cria uma separação da implementação real e com o que o teste está relacionado.
fonte
Então?
Não.
Vários testes podem ser independentes e todos falham devido ao mesmo bug. Isso é realmente normal. Muitos testes podem - indiretamente - testar algumas funcionalidades comuns. E tudo falha quando a funcionalidade comum é interrompida. Nada de errado com isso.
Os testes de unidade são definidos como classes precisamente para que eles possam compartilhar código facilmente, como um registro fictício comum usado para testar a atualização e a exclusão.
fonte
Você pode usar uma estrutura simulada ou um 'ambiente' com um banco de dados na memória. A última é uma classe em que você pode criar tudo o que precisa para fazer o teste passar, antes da execução do teste.
Prefiro o último - os usuários podem ajudá-lo a inserir alguns dados para que seus testes se tornem mais próximos do mundo real.
fonte