É uma prática ruim impor uma ordem de execução para testes de unidade?

81

Estou escrevendo testes para um projeto que consiste em vários submódulos. Cada caso de teste que escrevi é executado independentemente um do outro e eu limpo todos os dados entre os testes.

Embora os testes sejam executados independentemente, estou pensando em impor uma ordem de execução, pois alguns casos exigem mais de um submódulo. Por exemplo, um submódulo está gerando dados e outro está executando consultas nos dados. Se o submódulo que gera os dados contiver um erro, o teste para o submódulo de consulta também falhará, mesmo se o submódulo funcionar bem.

Não consigo trabalhar com dados fictícios, pois a principal funcionalidade que estou testando é a conexão com um servidor remoto de caixa preta, que obtém apenas os dados do primeiro submódulo.

Neste caso, é correto impor uma ordem de execução para os testes ou é uma prática ruim? Eu sinto que há um cheiro nessa configuração, mas não consigo encontrar uma maneira melhor de contornar isso.

edit: a questão é de Como estruturar testes em que um teste é a configuração de outro teste? como o "anterior" teste não é uma configuração, mas testa o código que executa a configuração.

    
por Ali Rasim Kocal 03.04.2018 / 15:02
fonte

6 respostas

234

I can not work with dummy data, as the main functionality I am testing is the connection to a black box remote server, which only gets the data from the first submodule.

Esta é a parte fundamental para mim. Você pode falar sobre "testes unitários" e "executar independentemente uns dos outros", mas todos eles parecem dependentes deste servidor remoto e dependem do "primeiro submódulo". Então tudo soa strongmente acoplado e dependente do estado externo. Como tal, você está, de fato, escrevendo testes de integração. Fazer com que esses testes sejam executados em uma ordem específica é bastante normal, pois são altamente dependentes de fatores externos. Um teste ordenado, com a opção de sair cedo do teste se algo der errado, é perfeitamente aceitável para testes de integração.

Mas também valeria a pena dar uma olhada na estrutura do seu aplicativo. Ser capaz de ridicularizar o primeiro sub-módulo e o servidor externo permitiria que você escrevesse testes de unidade verdadeiros para todos os outros sub-módulos.

    
por 03.04.2018 / 15:57
fonte
32

Sim, é uma prática ruim.

Geralmente, um teste unitário destina-se a testar uma única unidade de código (por exemplo, uma única função baseada em um estado conhecido).

Quando você deseja testar uma cadeia de eventos que pode acontecer na natureza, você deseja um estilo de teste diferente, como um teste de integração. Isso é ainda mais verdadeiro se você estiver dependendo de um serviço de terceiros.

Para testar coisas como esta, você precisa descobrir uma maneira de injetar os dados fictícios, por exemplo, implementando uma interface de serviço de dados que espelha a solicitação da web, mas retorna dados conhecidos de um arquivo de dados fictício local.

    
por 03.04.2018 / 15:07
fonte
16

A ordem de execução imposta que você propõe só faz sentido se você também interromper o teste após a primeira falha.

Anular a execução de teste na primeira falha significa que cada teste pode descobrir apenas um único problema e não pode encontrar novos problemas até que todos os problemas anteriores tenham sido corrigidos. Se o primeiro teste a ser executado encontrar um problema que leve um mês para ser corrigido, então, durante esse mês, efetivamente, nenhum teste será executado.

Se você não interromper a execução de teste na primeira falha, a ordem de execução imposta não lhe dará nada porque cada teste com falha precisa ser investigado de qualquer maneira. Mesmo que seja apenas para confirmar que o teste no submódulo de consulta está falhando por causa da falha que também foi identificada no submódulo de geração de dados.

O melhor conselho que posso dar é escrever os testes de tal forma que seja fácil identificar quando uma falha em uma dependência está fazendo com que o teste falhe.

    
por 03.04.2018 / 15:32
fonte
7

O cheiro a que você está se referindo é a aplicação do conjunto errado de restrições e regras aos seus testes.

Os testes de unidade geralmente são confundidos com "testes automatizados" ou "testes automatizados por um programador".

Os testes de unidade devem ser pequenos, independentes e rápidos.

Algumas pessoas leem incorretamente isso como "testes automatizados escritos por um programador devem ser pequenos independentes e rápidos" . Mas isso significa simplesmente que, se seus testes não são pequenos, independentes e rápidos, eles não são Testes Unitários e, portanto, algumas das regras para Testes Unitários não devem, não podem ou não devem se candidatar a seus testes. . Um exemplo trivial: você deve executar seus Testes Unitários após cada compilação, o que você não deve fazer para testes automatizados que não são rápidos.

Embora seus testes não sejam Testes de Unidade, você pode pular uma regra e ter permissão para ter alguma interdependência entre os testes; você também descobriu que há outras regras que você pode ter perdido e precisará reintroduzir - algo para o escopo de outra pergunta.

    
por 04.04.2018 / 11:10
fonte
6

Como mencionado acima, o que você está executando parece ser um teste de integração, mas você afirma que:

For example, a submodule is generating data, and another one is running queries on the data. If the submodule generating the data contains an error, the test for the query submodule will also fail, even if the submodule itself works fine.

E isso pode ser um bom lugar para começar a refatorar. O módulo que executa consultas nos dados não deve depender de uma implementação concreta do primeiro módulo (gerador de dados). Em vez disso, seria melhor injetar uma interface contendo os métodos para obter esses dados e isso pode ser ridicularizado para testar as consultas.

por exemplo,

Se você tem:

class Queries {

    int GetTheNumber() {
        var dataModule = new Submodule1();
        var data = dataModule.GetData();
        return ... run some query on data
    }
}

Em vez disso, prefira:

interface DataModule {
    Data GetData();
}


class Queries {

    IDataModule _dataModule;

    ctor(IDataModule dataModule) {
       _dataModule = dataModule;
    }

    int GetTheNumber() {
        var data = _dataModule.GetData();
        return ... run some query on data
    }
}

Isso elimina a dependência de consultas na sua fonte de dados e permite que você configure testes de unidade facilmente reproduzíveis para determinados cenários.

    
por 04.04.2018 / 11:58
fonte
6

As outras respostas mencionam que os testes de pedidos são ruins (o que é verdade na maioria das vezes), mas há uma boa razão para impor o pedido na execução do teste: garantir que seus testes lentos (isto é, testes de integração) sejam executados mais rapidamente testes (testes que não dependem de outros recursos externos). Isso garante que você execute mais testes mais rapidamente, o que pode acelerar o ciclo de feedback.

    
por 04.04.2018 / 16:10
fonte