Apresentando o teste em Python. Parte 2

Olá pessoal!

Continuamos o artigo sobre como familiarizar-se com os testes no Python, que preparamos para você como parte de nosso curso de desenvolvedor em Python .

Testando para o Django e o Flask Web Framework

Se você escreve testes para aplicativos da Web usando uma das estruturas populares, por exemplo, Django ou Flask, vale a pena lembrar as diferenças importantes na escrita e execução desses testes.

Como eles diferem de outros aplicativos

Pense no código que você deseja testar em um aplicativo da web. Todas as rotas, visualizações e modelos exigem muita importação e conhecimento sobre a estrutura usada.
Isso é semelhante ao teste de um carro, discutido na primeira parte do tutorial: antes de realizar testes simples, como a verificação dos faróis, você precisa ligar o computador no carro.

O Django e o Flask simplificam esta tarefa e fornecem uma estrutura de teste baseada em unittest. Você pode continuar escrevendo testes da maneira usual, mas execute-os de maneira um pouco diferente.



Como usar o Django Test Executor

O modelo startapp do Django cria o arquivo tests.py no diretório do aplicativo. Se ainda não estiver lá, crie-o com o seguinte conteúdo:

from django.test import TestCase class MyTestCase(TestCase): # Your test methods 

A principal diferença dos exemplos anteriores é que você precisa herdar do django.test.TestCase , não unittest.TestCase . A API dessas classes é a mesma, mas a classe Django TestCase configura tudo para o teste.

Para executar um conjunto de testes, use o teste manage.py vez de unittest na linha de comando:

 $ python manage.py test 

Se você precisar de vários arquivos de teste, substitua tests.py pela pasta tests, coloque um arquivo vazio chamado __init__.py e crie test_*.py arquivos test_*.py . O Django irá detectá-los e executar.

Mais informações estão disponíveis no site de documentação do Django .

Como usar o Unittest e o Flask

Para trabalhar com o Flask, um aplicativo deve ser importado e colocado no modo de teste. Você pode criar um cliente de teste e usá-lo para enviar solicitações para qualquer rota em seu aplicativo.

O cliente de teste é instanciado no método setUp do seu caso de teste. No exemplo a seguir, my_app é o nome do aplicativo. Não se preocupe se você não sabe o que o setUp faz. Vamos dar uma olhada na seção "Scripts de teste mais avançados".
O código no arquivo de teste terá a seguinte aparência:

 import my_app import unittest class MyTestCase(unittest.TestCase): def setUp(self): my_app.app.testing = True self.app = my_app.app.test_client() def test_home(self): result = self.app.get('/') # Make your assertions 

Você pode executar casos de teste usando o python -m unittest discover.

Mais informações estão disponíveis no site de documentação do Flask.

Scripts de teste mais avançados

Antes de começar a criar testes para o seu aplicativo, lembre-se das três etapas principais de qualquer teste:

  1. Criação de parâmetros de entrada;
  2. Execução de código, recebendo dados de saída;
  3. Comparação de dados de saída com o resultado esperado;

Isso pode ser mais complicado do que criar um valor estático para os dados de origem, como uma sequência ou número. Às vezes, seu aplicativo requer uma instância de uma classe ou contexto. O que fazer neste caso?

Os dados que você cria como fonte são chamados de dispositivo elétrico. Criar e reutilizar equipamentos é uma prática comum.

Executar o mesmo teste várias vezes com valores diferentes em antecipação ao mesmo resultado é chamado de parametrização.

Tratamento de falhas esperadas

Antes, quando compilamos uma lista de scripts para testar sum() , surgiu a pergunta: o que acontece quando fornecemos um valor inválido, por exemplo, um único número inteiro ou uma string?

Nesse caso, espera-se que sum() gere um erro. Se ocorrer um erro, o teste falhará.

Existe uma maneira específica de lidar com os erros esperados. Você pode usar .assertRaises() como um gerenciador de contexto e executar etapas de teste dentro do bloco with :

 import unittest from my_sum import sum class TestSum(unittest.TestCase): def test_list_int(self): """ ,       """ data = [1, 2, 3] result = sum(data) self.assertEqual(result, 6) def test_list_fraction(self): """ ,       """ data = [Fraction(1, 4), Fraction(1, 4), Fraction(2, 5)] result = sum(data) self.assertEqual(result, 1) def test_bad_type(self): data = "banana" with self.assertRaises(TypeError): result = sum(data) if __name__ == '__main__': unittest.main() 

Este caso de teste será passado apenas se sum(data) gerar um TypeError. Você pode substituir TypeError por qualquer outro tipo de exceção.

Isolamento de comportamento de aplicativos

Na última parte do tutorial, falamos sobre efeitos colaterais. Eles complicam o teste de unidade, pois cada execução de teste pode produzir um resultado diferente ou pior - um teste pode afetar o estado de todo o aplicativo e causar a falha de outro teste!

Existem algumas técnicas simples para testar partes de um aplicativo com muitos efeitos colaterais:

  • Refatoração de código de acordo com o Princípio de Responsabilidade Única;
  • Zombando de todos os métodos e chamadas de função para eliminar efeitos colaterais;
  • Usando testes de integração em vez de testes de unidade para este fragmento do aplicativo.
  • Se você não conhece o moking, confira alguns ótimos exemplos de testes de CLI do Python .

Escrevendo testes de integração

Até agora, prestamos mais atenção aos testes de unidade. O teste de unidade é uma ótima maneira de criar código previsível e estável. Mas, no final, seu aplicativo deve funcionar na inicialização!

O teste de integração é necessário para verificar a colaboração de vários componentes de aplicativos. Esses testes podem exigir a atuação do comprador ou usuário:

  • Chamar API REST HTTP;
  • Chamada de API Python;
  • Chamada de serviço da Web;
  • Execute a linha de comando.

Todos esses tipos de testes de integração podem ser gravados da mesma maneira que os testes de unidade, seguindo o modelo Parâmetros de entrada, Execução, Aprovação. A diferença mais significativa é que os testes de integração testam simultaneamente mais componentes, o que significa que eles levarão a mais efeitos colaterais do que os testes de unidade. Além disso, os testes de integração exigem mais equipamentos, como um banco de dados, soquete de rede ou arquivo de configuração.

Portanto, é recomendável separar testes de unidade e testes de integração. A criação de fixtures para os de integração, por exemplo, um banco de dados de teste ou casos de teste em si, leva muito mais tempo que a execução de testes de unidade; portanto, você deve executar testes de integração antes de entrar em produção em vez de executá-los sempre que confirmar.

A maneira mais simples de separar os testes de unidade e integração é colocá-los em pastas diferentes.

project/

├── my_app/
│ └── __init__.py

└── tests/
|
├── unit/
| ├── __init__.py
| └── test_sum.py
|
└── integration/
├── __init__.py
└── test_integration.py


Você pode executar um grupo específico de testes de maneiras diferentes. Um sinalizador para especificar o diretório de origem, -s, pode ser adicionado à descoberta mais unida com um caminho que contém testes:

 $ python -m unittest discover -s tests/integration 

O unittest exibirá todos os resultados no diretório tests / integration.

Testando aplicativos orientados a dados

Muitos testes de integração requerem dados de back-end, como um banco de dados com valores específicos. Suponha que você precise de um teste para verificar a operação correta do aplicativo com mais de 100 clientes no banco de dados ou para verificar a exatidão da exibição da página do pedido, mesmo que todos os nomes das mercadorias estejam em japonês.

Esses tipos de testes de integração dependerão de vários equipamentos de teste para garantir sua repetibilidade e previsibilidade.

Os dados de teste devem ser armazenados na pasta de acessórios dentro do diretório de testes de integração para enfatizar sua “testabilidade”. Em seguida, nos testes, você pode carregar os dados e executar o teste.

Aqui está um exemplo de uma estrutura de dados que consiste em arquivos JSON:

project/

├── my_app/
│ └── __init__.py

└── tests/
|
└── unit/
| ├── __init__.py
| └── test_sum.py
|
└── integration/
|
├── fixtures/
| ├── test_basic.json
| └── test_complex.json
|
├── __init__.py
└── test_integration.py


No caso de teste, você pode usar o método .setUp () para carregar dados de teste do arquivo de fixação de uma maneira conhecida e executar vários testes com esses dados. Lembre-se de que você pode armazenar vários casos de teste em um arquivo Python, o unittest os encontrará e executará. Você pode ter um caso de teste para cada conjunto de dados de teste:

 import unittest class TestBasic(unittest.TestCase): def setUp(self): # Load test data self.app = App(database='fixtures/test_basic.json') def test_customer_count(self): self.assertEqual(len(self.app.customers), 100) def test_existence_of_customer(self): customer = self.app.get_customer(id=10) self.assertEqual(customer.name, "Org XYZ") self.assertEqual(customer.address, "10 Red Road, Reading") class TestComplexData(unittest.TestCase): def setUp(self): # load test data self.app = App(database='fixtures/test_complex.json') def test_customer_count(self): self.assertEqual(len(self.app.customers), 10000) def test_existence_of_customer(self): customer = self.app.get_customer(id=9999) self.assertEqual(customer.name, u"バナナ") self.assertEqual(customer.address, "10 Red Road, Akihabara, Tokyo") if __name__ == '__main__': unittest.main() 

Se seu aplicativo depende de dados de um local remoto, como uma API remota, verifique se os testes são repetíveis. O desenvolvimento pode estar atrasado devido a testes que falharam ao desativar a API e problemas de comunicação. Nesses casos, é melhor armazenar dispositivos remotos localmente para serem chamados de volta e enviados ao aplicativo.

A biblioteca de requests possui um pacote de respostas gratuito que permite criar dispositivos de resposta e salvá-los em pastas de teste. Saiba mais na página do GitHub .

A próxima parte será sobre testes em vários ambientes e automação de testes.

O FIM

Comentários / perguntas são sempre bem-vindos. Aqui ou vá para Stas em um dia aberto .

Source: https://habr.com/ru/post/pt433572/


All Articles