Alura > Cursos de Programação > Cursos de Python > Conteúdos de Python > Primeiras aulas do curso Python e TDD: explorando testes unitários

Python e TDD: explorando testes unitários

Testes automatizados - Apresentação

Seja bem-vindo(a) a este curso de Python focado em testes e em TDD. Meu nome é Bruno Divino, faço parte do time de instrutores da Alura e vou te acompanhar nessa jornada de aprendizagem. Minha autodescrição: tenho cabelos castanhos, bigode, olhos verde e pele clara. Estou sentado em uma cadeira verde e, ao fundo, há uma parede azul.

Este curso é destinado para pessoas que têm algum conhecimento prévio de Python e que estejam interessadas em desenvolver vários projetos utilizando essa linguagem, sejam eles voltados para Web ou dedicados a outras questões, como automatização e DevOps. A utilização de testes é bem-vinda em várias áreas, trata-se de um assunto bastante generalista e pode ser usado em diversos contextos. Contanto que seja Python, podemos desenvolver bastante com testes.

O único pré-requisito para acompanhar este treinamento é o conhecimento de Python, principalmente direcionado a orientação a objetos. Ao longo do curso, trabalharemos com um projeto que utiliza classes, métodos dentro de classes e instanciação de objetos, por isso, é interessante que você tenha conhecimento prévio relacionado a esse paradigma tão importante da linguagem Python.

No decorrer do curso, ajudaremos a Dominique, uma pessoa desenvolvedora iniciante na empresa Bytebank. O chefe dela a encarregou de desenvolver um projeto e ela tem algumas dúvidas a respeito do que são testes e como eles podem melhorar o processo de desenvolvimento de seus projetos. Então, nos dispomos a ensiná-la sobre o que são e para que servem os testes, bem como de que se trata a metodologia TDD (Test-driven Development). Ao longo desse processo, nós também aprenderemos muito — afinal de contas, a melhor forma de aprender é ensinando.

Agora que sabemos tudo que será abordado neste treinamento, podemos dispensar a ansiedade e relaxar um pouco, pegar um copo de água e nos preparar para começar nossos estudos!

Testes automatizados - O projeto Bytebank

Antes de começarmos o curso, vamos conhecer a história da Dominique.

A Dominique é uma dev iniciante que trabalha na empresa Bytebank, uma fintech (banco digital focado em tecnologia e finanças). Ela está muito empolgada, pois este é seu primeiro emprego como pessoa desenvolvedora. O chefe dela notou seu entusiasmo, então já lhe atribuiu um pequeno projeto que, posteriormente, fará parte de um projeto ainda maior da empresa.

O projeto da Dominique é desenvolver uma classe que contenha as informações dos funcionários do Bytebank. Ela já elaborou o código e, em breve, vamos revisá-lo.

Antes disso, ela pediu nossa ajuda para decifrar alguns termos que sempre encontra no seu dia a dia de trabalho. Ao seu redor, as pessoas devs mais experientes comentam sobre testes, testes unitários e metodologia do TDD e a Dominique nos disse que não sabe exatamente do que se tratam esses termos. Então, além de revisar seus códigos e ajudá-la a avançar em seus projetos, também vamos sanar essas dúvidas e aprender bastante com o que temos a ensinar. Afinal, a melhor forma de aprender é ensinando!

A princípio, vamos analisar o projeto da Dominique. Optei por utilizar o PyCharm como IDE. Para facilitar o acompanhamento do curso, é preferível que você também use, porém não é imprescindível — fica a seu critério.

Abrindo o projeto da Dominique no PyCharm, vamos revisar a estrutura de diretórios, no painel à esquerda. Temos uma pasta chamada codigo e, dentro dela, há o arquivo bytebank.py, onde ela criou a classe Funcionario. Vamos examiná-la, a seguir.

Conforme as regras de negócio passadas pelo chefe dela, a classe Funcionario deve conter o nome, a data de nascimento e o salário do funcionário. Entre as linhas 9 e 15, notamos que a Dominique já criou um property com nome e outro com salário. Além disso, entre as linhas 17 e 25, há dois métodos que compreendem a regra de negócio da classe (idade e calcular_bonus).

O método idade tem um nome autoexplicativo: ele captura a data de nascimento do funcionário e calcula a idade dele a partir do ano atual. Mais adiante, focaremos no método calcular_bonus. Nas linhas 27 e 28, temos a função __str__ que retorna as informações completas do objeto.

Antes de testar a classe, vamos acessar o terminal. Notaremos que não existe um ambiente virtual do Python instanciado. Ao desenvolver projetos Python, é interessante termos um ambiente virtual funcionando para não criar conflitos entre diferentes projetos presentes na nossa máquina. Portanto, vamos fazer essa instanciação.

Basta executar o comando python3 -m venv venv. O primeiro venv refere-se a virtual environment, o ambiente virtual que estamos criando. O segundo venv é o nome do ambiente. Podemos escolher outro nome, porém por padrão mantém-se "venv", por isso venv venv.

Ao pressionar a tecla "Enter", o ambiente virtual será criado e o diretório venv aparecerá na estrutura de diretórios do projeto, no painel à esquerda. Para ativar o ambiente, rodaremos source 'venv/bin/activate. A partir de agora, haverá uma tag (venv) no início da linha no terminal, indicando que estamos nesse ambiente virtual.

Na sequência, podemos testar o código da Dominique. Na pasta codigo, criaremos um arquivo chamado main.py e nele chamaremos um objeto para testar a classe Funcionario. Primeiramente, precisamos importar a classe:

from codigo.bytebank import Funcionario

Depois, criaremos o funcionário lucas. Seu nome será Lucas Carvalho; a data de nascimento, 2000; e o salário inicial, R$1000,00:

from codigo.bytebank import Funcionario

lucas = Funcionario('Lucas Carvalho', 2000, 1000)

Ao digitar Funcionario(, a IDE exibirá uma indicação do próximo argumento que devemos fornecer.

Para visualizarmos o objeto, utilizaremos a função print:

from codigo.bytebank import Funcionario

lucas = Funcionario('Lucas Carvalho', 2000, 1000)

print(lucas)

Em seguida, para executar o código, clicaremos com o botão direito sobre o código e selecionaremos "Run 'main'". Outra opção é usar o atalho "Ctrl + Shift + F10". Como esperado, teremos no terminal as informações do funcionário Lucas.

Agora, vamos modificar a linha 5 do código, para testar a função idade:

from codigo.bytebank import Funcionario

lucas = Funcionario('Lucas Carvalho', 2000, 1000)

print(lucas.idade)

Como estamos em 2022, o resultado será 22 — o código está funcionando.

No entanto, se prestarmos atenção, há um ponto estranho na regra de negócio: o funcionário deve informar a data de nascimento (composta por dia, mês e ano) e indicamos somente o ano em que o Lucas nasceu. Vamos alterar a linha 3, designando a data completa e a transformando em uma string:

from codigo.bytebank import Funcionario

lucas = Funcionario('Lucas Carvalho', '13/03/2000', 1000)

print(lucas.idade)

Ao rodar o código novamente, ocorrerá um erro. A regra de negócio está falha, vamos consertá-la no próximo vídeo.

Testes automatizados - Criando o primeiro teste

No vídeo passado, encontramos um erro no nosso código, então vamos analisar o arquivo main.py para entender o que aconteceu.

Estamos utilizando um objeto: o funcionário lucas que criamos na linha 3, que recebe 'Lucas Carvalho' como nome, '13/03/2000' como data de nascimento e 1000 como salário. Depois, na linha 5, chamamos o método idade para calcular a idade do funcionário.

Inicialmente, ao informar a data de nascimento do Lucas, colocamos apenas o ano. No entanto, notamos que isso seria uma infração à regra de negócio. Quando pensamos em data de nascimento, trata-se de dia, mês e ano — não apenas o ano! Logo, substituímos o ano 2000 por uma string '13/03/2000':

from codigo.bytebank import Funcionario

lucas = Funcionario('Lucas Carvalho', '13/03/2000', 1000)

print(lucas.idade)

Porém, essa mudança causou um erro no método idade. A seguir, vamos verificar o funcionamento dessa função, no arquivo bytebank.py.

Na linha 18, através da biblioteca date do Python, recebemos o ano atual (no caso, 2022). Para calcular a idade, na linha 19, a data de nascimento informada pelo funcionário é subtraída do ano atual. Nessa linha, a Dominique usou o método int para transformar a data de nascimento em um int, porém se usarmos o formato 13/03/2000, com barras, esse valor não será transformado em int.

Então, vamos alterar o método idade para torná-lo funcional novamente. "Quebraremos" a data de nascimento em três partes, formando uma lista com três itens. O primeiro item será o dia; o segundo, o mês; e o terceiro, o ano.

Na linha 18, criaremos uma variável chamada data_nascimento_quebrada e atribuiremos a ela o resultado de self._data_nascimento.split('/'):

# código anterior omitido

def idade(self):
    data_nascimento_quebrada = self._data_nascimento.split('/')
    ano_atual = date.today().year
    return ano_atual - int(self._data_nascimento)

# código posterior omitido

No vídeo, há um pequeno erro de digitação na variável data_nascimento_quebrada, mas ele já foi corrigido nas transcrições.

O split é um método do Python utilizado para formatar strings. Com ele, vamos "quebrar" a string em determinados pontos e retornar uma lista com essas partes "quebradas". Dentro do split, passaremos o caractere usado como referência para realizar essas quebras. No nosso caso, é a barra (/). Assim, teremos uma lista com três itens: dia, mês e ano.

Em seguida, vamos criar uma variável chamada ano_nascimento e igualá-la ao último item da lista data_nascimento_quebrada. Usaremos data_nascimento_quebrada[-1] para selecionar o último item da lista:

# código anterior omitido

def idade(self):
    data_nascimento_quebrada = self._data_nascimento.split('/')
    ano_nascimento = data_nascimento_quebrada[-1]
    ano_atual = date.today().year
    return ano_atual - int(self._data_nascimento)

# código posterior omitido

Por fim, na linha 21, substituiremos self._data_nascimento por ano_nascimento:

# código anterior omitido

def idade(self):
    data_nascimento_quebrada = self._data_nascimento.split('/')
    ano_nascimento = data_nascimento_quebrada[-1]
    ano_atual = date.today().year
    return ano_atual - int(ano_nascimento)

# código posterior omitido

Na sequência, vamos executar o código de main.py. Basta abrir o arquivo, clicar com botão direito do mouse sobre ele e selecionar "Run 'main'" (ou usar o atalho "Ctrl + Shift + F10"). O retorno será a idade de Lucas, 22 anos. Ou seja, após as alterações, o código voltou a funcionar.

Assim, nós instanciamos lucas como um funcionário e exibimos sua idade no terminal, porém o chefe da Dominique gostaria que transformássemos esse processo em um teste automatizado. Então, vamos comentar as linhas 3 e 5, e desenvolver esses testes.

A partir da linha 7, criaremos um método chamado teste_idade, em que instanciaremos um novo funcionário. O nome dele será 'Teste', a data de nascimento será '13/03/2000' e o salário, 1111:

from codigo.bytebank import Funcionario

#lucas = Funcionario('Lucas Carvalho', '13/03/2000', 1000)

#print(lucas.idade)

def teste_idade():
    funcionario_teste = Funcionario('Teste', '13/03/2000', 1111)

Em seguida, usando o prefixo f, vamos printar o valor formatado:

# código anterior omitido

def teste_idade():
    funcionario_teste = Funcionario('Teste', '13/03/2000', 1111)
    print(f'Teste = {funcionario_teste.idade()}')

Acabamos de criar um teste automatizado. Informamos alguns valores e o teste indicará se tudo está funcionando como esperado. Antes de rodar o código, precisamos chamar teste_idade:

# código anterior omitido

def teste_idade():
    funcionario_teste = Funcionario('Teste', '13/03/2000', 1111)
    print(f'Teste = {funcionario_teste.idade()}')

teste_idade()

Por fim, vamos executar o código. O retorno estará correto! Sendo assim, o nosso primeiro teste automatizado está pronto. Vale notar que ele está dentro de um método, onde poderíamos aplicar várias outras lógicas mirabolantes para testar o código da Dominique.

Visto que tudo está funcionando corretamente, podemos remover as linhas comentadas do arquivo para deixá-lo mais enxuto:

from codigo.bytebank import Funcionario

def teste_idade():
    funcionario_teste = Funcionario('Teste', '13/03/2000', 1111)
    print(f'Teste = {funcionario_teste.idade()}')

Então, falamos bastante sobre testes e criamos um teste automatizado, mas o que são testes automatizados, afinal? Se há testes automatizados, quer dizer que existem testes não automatizados? Quais tipos de testes existem? Vamos explorar essas questões na próxima aula.

Sobre o curso Python e TDD: explorando testes unitários

O curso Python e TDD: explorando testes unitários possui 150 minutos de vídeos, em um total de 47 atividades. Gostou? Conheça nossos outros cursos de Python em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Python acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas