~/Organização de testes unitários com Django

Esse é o terceiro post da série Pipeline de CI/CD com Django e Github Actions, nesse post eu vou trazer uma sugestão de como organizar testes em uma aplicação Django.

Confira a lista de todos os posts da série:

Teste unitário?

Como o nome sugere, é um teste de uma unidade. E o que é considerado uma unidade? Um bloco de código, como por exemplo um modelo é uma função, uma classe enfim.

A intenção em criar testes unitários é que você tenha principalmente um passo a passo de como resolver um problema.

1 - Configurando projeto inicial:

Se você está acompanhando essa série de posts, você deve ter lido o último tutorial, onde criamos um projeto do zero em Django.

Eu disponibilizei o projeto no meu Github para que possa agilizar o processo. Então clone o repositório e depois baixe a branch feature/init para iniciarmos a brincadeira.

❯ git clone https://github.com/f0rmig4/pipeline-django.git
❯ cd pipeline-django
❯ git fetch origin feature/init:feature/init
❯ git checkout feature/init

A estrutura em árvore do projeto criado é semelhante à seguinte.

├── manage.py
    ├── core
    └── question
       ├── migrations
       ├── admin.py
       ├── apps.py
       ├── models.py
       ├── tests.py
       └── views.py

O diretório 'core' é onde está o projeto Django e já o diretório 'question' onde encontra-se a nossa aplicação que vamos trabalhar.

Geralmente os testes de cada aplicação devem estar dentro do arquivo question/tests.py, não existe problema em colocar os seus testes aqui, entretanto dentro de uma aplicação podemos realizar vários tipos de testes, como por exemplo, testes unitários para modelos, views e formulários.

Se você incluir todos os seus testes no arquivo tests.py essa prática pode se tornar um pouco confusa, conforme o projeto cresce, aumenta o nível de complexidade. Pensando nisso eu realizei uma pesquisa e através de outras experiências de projetos eu cheguei na seguinte estrutura:

├── manage.py
    ├── core
    └── question
       ├── migrations
       ├── admin.py
       ├── apps.py
       ├── models.py
       ├── tests
            └── unittest
                ├── tests_models.py
                ├── tests_views.py
                └── tests_forms.py
        └── views.py

Como nossa aplicação pode ter vários tipos de testes, como unitários, integração, eu sugiro criar um diretório para cada tipo de testes e posteriormente dividir as rotinas com as suas respectivas responsabilidades, como o exemplo acima.

Essa estrutura é sugerida para cada aplicação, caso tenhamos 10 aplicações no projeto devemos seguir essa estrutura. Obviamente não é uma regra e sim apenas uma sugestão de padronização e organização.

2 - Criando nossos testes

Antes de criar os nossos testes devemos iniciar criando nossos modelos, abaixo segue o exemplo criado para o nosso caso de estudo. Altere o arquivo question/models.py

from django.db import models
     
class Question(models.Model):
    name = models.CharField('Name', max_length=100)
    amount = models.PositiveIntegerField('Amount')
    
    def __str__(self):
        return self.name

Com o modelo criado devemos criar nossas migrations:

❯ python3 manage.py makemigrations
❯ python3 manage.py migrate

Uma dica bacana, caso você esteja utilizando PostgreSQL o usuário do banco de dados deve ter permissão de criar databases, durante a execução dos testes é criado um banco de dados temporária e depois também é destruído assim que os testes rodam. No caso do SQLite não será necessário.

Agora vamos criar nosso primeiro teste, dentro do arquivo question/tests/tests_models.py

from django.test import TestCase
from ..models import Question
    
class QuestionTestCase(TestCase):
    def setUp(self):
        Question.objects.create(
            name="You are you?",
            amount=12
        )
    
    def test_return_str(self):
        q = Question.objects.get(name="You are you?")
        self.assertEquals(q.__str__(), "You are you?")

Algumas considerações do código acima:

  • O primeiro passo a ser dado é importar o model;
  • O método setUp, é executado toda vez que a classe é chamada, neste exemplo estamos utilizando para criar uma question antes de rodar o teste;
  • Uma coisa importante todo método de teste deve iniciar com "test_";
  • No exemplo o test_return_str vai assegurar que minha controle sempre vai retornar o valor "You are you?".

Agora vamos no terminal e vamos rodar os testes da nossa aplicação question:

❯ python3 manage.py test question

Caso o seu teste tenha rodado com sucesso, devemos ter um retorno semelhante a esse exemplo:

❯ python3 manage.py test question
     
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
    
OK
Destroying test database for alias 'default'...

Chegamos ao fim dessa jornada, vimos um exemplo muito simples de como organizar nossas rotinas de testes para uma aplicação Django.

No próximo post da série vamos, então, por fim configurar nossa CI/CD com Django e Github Actions.

Que a força esteja com vocês!


Published Jul 17, 2021 by f0rmig4