Configurando o Ambiente de Desenvolvimento
Objetivos dessa aula:
- Introdução ao ambiente de desenvolvimento (terminal, ferramentas, etc.)
- Instalação do FastAPI e suas dependências
- Configuração das ferramentas de desenvolvimento
- Execução do primeiro "Hello, World!" com FastAPI com testes!
Caso prefira ver a aula em vídeo
Esse aula ainda não está disponível em formato de vídeo, somente em texto ou live!
Aula Slides Código Quiz Exercícios
Nesta aula, iniciaremos nossa jornada na construção de uma API com FastAPI. Partiremos do básico, configurando nosso ambiente de desenvolvimento. Discutiremos desde a escolha e instalação da versão correta do Python até a instalação e configuração do Poetry, um gerenciador de pacotes e dependências para Python. Além disso, instalaremos e configuraremos uma série de ferramentas de desenvolvimento úteis, como Ruff, pytest e Taskipy.
Após configurado o nosso ambiente, criaremos nosso primeiro programa "Hello, World!" com FastAPI. Isso nos permitirá confirmar que tudo está funcionando corretamente. E, finalmente, exploraremos uma parte crucial do Desenvolvimento Orientado por Testes (TDD), escrevendo nosso primeiro teste com Pytest.
Ambiente de Desenvolvimento
Para iniciar esse curso você precisa de algumas ferramentas instaladas:
- Um editor de texto a sua escolha (Usarei o GNU/Emacs)
- Um terminal a sua escolha (Usarei o Terminator)
- Python, em uma versão oficialmente suportada, atualmente 3.9+
- Git: Para gerenciar versões do nosso projeto. (Caso precise de um tutorial legal sobre git, o TeoMeWhy fez uma playlist muito legal, totalmente de graça)
- Docker: Para criar um container da nossa aplicação (caso não tenha nenhuma experiência com docker a Linuxtips tem uma playlist completa e grátis sobre docker no canal deles no YouTube)1
- OPCIONAL (extremamente recomendado): O gh para criar o repositório e fazer alterações sem precisar acessar a página do Github
🚨🚨 Caso você precise de ajuda com a instalação dessas ferramentas, temos um apêndice especial para te ajudar com isso!. Basta clicar em achar a ferramenta que deseja instalar! 🚨🚨
Sobre as verões das bibliotecas instaladas
A última atualização dos pacotes deste material foi feita em: 18/03/2025 às 09:21.Caso você esteja lendo este material em um futuro distante ou enfrente erros de instalação, o apêndice C contém todas as versões usadas no ambiente de forma detalhada.
pipx
O pipx é uma ferramenta usada para instalar e executar ferramentas Python globalmente no sistema de forma segura. Diferente do pip, que instala ferramentas sem um ambiente virtual (por padrão) e pode "sujar" nosso ambiente, o pipx
cria um ambiente virtual e isola cada ferramenta dentro dele, facilitando a instalação de pacotes globais.
Em nosso projeto, usaremos o pipx para instalar ferramentas globais e executar algumas que serão usadas apenas uma vez.
Para instalar o pipx:
Dessa forma, a única dependência global que teremos no nosso sistema será o próprio pipx
.
Existem outras formas de instalar o pipx
Caso você tenha um gerenciador de pacotes no seu sistema operacional, é extremamente recomendado que você instale o pipx
por ele.
As instruções de instalação para cada sistema estão disponíveis na documentação do projeto.
Para o nosso sistema reconhecer o caminho das ferramentas instaladas via pipx
podemos executar o comando:
- Adiciona o diretório dos ambientes virtuais do
pipx
aoPATH
do sistema.
Esse comando adiciona ao PATH
do sistema todos os binários instalados pelo pipx
🚨. Portanto, lembre-se de reiniciar o shell após executá-lo.
Poetry
O Poetry é um gerenciador de projetos para Python. Ele pode nos ajudar em diversas etapas do ciclo de desenvolvimento, como a instalação de versões específicas do Python, a criação e manutenção de projetos (incluindo a definição de estruturas de pastas, o gerenciamento de ambientes virtuais e a instalação de bibliotecas), além de permitir a publicação de pacotes e muito mais.
No nosso projeto, ele será o componente central para agrupar e executar todas as tarefas relacionadas ao projeto.
Caso esse seja seu primeiro contato com o Poetry
Temos uma live de python explicando somente ele:
Instalação do poetry
A instalação do Poetry pode ser feita de diversas maneiras, mas a forma que recomendo, para uma instalação global e isolada em um ambiente virtual, é via pipx
:
- Cria um ambiente virtual isolado para poetry e o deixa disponível no sistema.
Comentários em blocos
Blocos de código costumam ter comentários com informações adicionais, como este:
Ao clicar em um bloco de comentário se abrirá, exibindo mais informações:
E, para facilitar nosso fluxo de trabalho com ambientes virtuais, vamos instalar uma extensão do Poetry para habilitar o shell:
- A função
self add
do Poetry instala extensões para podermos executar novos comandos com o Poetry, neste caso, o comandopoetry shell
, que nos permite entrar nos ambientes virtuais.
Essa extensão adiciona o comando poetry shell
, que habilita o ambiente virtual no terminal.
Gerenciamento de versões do Python
Após a instalação do Poetry, podemos utilizá-lo para gerenciar e instalar versões do Python que desejamos usar em um projeto. Para acompanhar este curso, a versão mínima do Python que você deve ter é a 3.11
, pois alguns recursos que utilizaremos foram introduzidos nessa versão.
Você pode, no entanto, instalar qualquer versão mais nova. Minha recomendação é sempre que possível, use a versão mais atualizada possível:
Para utilizarmos uma versão específica do Python em nosso ambiente, devemos solicitar ao Poetry que instale essa versão:
- Instala a última release da versão 3.13 do python
Uma resposta similar a esta deve ser retornada ao executar o comando:
Downloading and installing 3.13.2 (cpython) ... Done #(1)!
Testing 3.13.2 (cpython) ... Done
- 3.13.2 é a última release da versão lançada enquanto esscrevia esse material.
Para utilizarmos uma versão específica do Python em nosso ambiente, devemos solicitar ao Poetry que instale essa versão:
- Instala a última release da versão 3.12 do python
Uma resposta similar a esta deve ser retornada ao executar o comando:
Downloading and installing 3.12.9 (cpython) ... Done #(1)!
Testing 3.12.9 (cpython) ... Done
- 3.12.9 é a última release da versão lançada enquanto esscrevia esse material.
Para utilizarmos uma versão específica do Python em nosso ambiente, devemos solicitar ao Poetry que instale essa versão:
- Instala a última release da versão 3.11 do python
Uma resposta similar a esta deve ser retornada ao executar o comando:
Downloading and installing 3.11.11 (cpython) ... Done #(1)!
Testing 3.11.11 (cpython) ... Done
- 3.11.11 é a última release da versão lançada enquanto esscrevia esse material.
Dessa forma, garantimos que temos uma versão compatível do Python instalada.
Criando um projeto
Agora que temos o poetry e a versão do python que usaremos disponível, podemos iniciar a criação do nosso projeto. O primeiro passo é criar um novo projeto utilizando o Poetry, com o comando poetry new
. Em seguida, navegaremos até o diretório criado:
- Cria um pacote python chamado
fast_zero
no formatoflat
. Por padrão o poetry utiliza o formatosrc
. Mais informações sobre isso aqui
Ele criará uma estrutura de arquivos e pastas como essa:
Com a estrutura inicial do projeto criada e estando no do diretório do projeto, podemos informar ao Poetry que queremos usar a versão do Python que instalamos. Para isso, utilizamos o seguinte comando:
Em conjunto com essa instrução, devemos também especificar no Poetry que usaremos exatamente a versão 3.13
em nosso projeto. Para isso, alteramos o arquivo de configuração pyproject.toml na raiz do projeto:
- A expressão
">=3.13,<4.0"
significa que qualquer versão maior ou igual a3.13
e menor que 4.0 será válida para o projeto.
Em conjunto com essa instrução, devemos também especificar no Poetry que usaremos exatamente a versão 3.12
em nosso projeto. Para isso, alteramos o arquivo de configuração pyproject.toml na raiz do projeto:
- A expressão
">=3.12,<4.0"
significa que qualquer versão maior ou igual a3.12
e menor que 4.0 será válida para o projeto.
Em conjunto com essa instrução, devemos também especificar no Poetry que usaremos exatamente a versão 3.11
em nosso projeto. Para isso, alteramos o arquivo de configuração pyproject.toml na raiz do projeto:
- A expressão
">=3.11,<4.0"
significa que qualquer versão maior ou igual a3.11
e menor que 4.0 será válida para o projeto.
Dessa forma, garantimos que o Poetry usará a versão correta do Python ao criar o ambiente virtual para o nosso projeto.
Instalando o FastAPI
Com toda a base do nosso projeto pronta, podemos finalmente instalar o FastAPI:
- Cria o ambiente virtual com a versão que setamos no
env use
- Adiciona o FastAPI no nosso projeto e ambiente virtual
Primeira Execução de um "Hello, World!"
Uma coisa bastante interessante sobre o FastAPI é que ele é um framework web baseado em funções. Da mesma forma em que criamos funções tradicionalmente em python, podemos estender essas funções para que elas sejam servidas pelo servidor. Por exemplo:
Essa função em python basicamente retorna um dicionário com uma chave chamada 'message'
e uma mensagem 'Olá Mundo!'
. Se adicionarmos essa função em novo arquivo chamado app.py
no diretório fast_zero
. Podemos fazer a chamada dela pelo terminal interativo (REPL):
De forma tradicional, como todas as funções em python.
Dica: Como abrir o terminal interativo (REPL)
Para abrir o terminal interativo com o seu código carregado, você deve chamar o Python no terminal usando -i:
O interpretador do Python executa o código do arquivo e retorna o shell após executar tudo que está escrito no arquivo.
Para o nosso caso específico, como o nome do arquivo é fast_zero/app.py
, devemos executar esse comando no terminal:
Desta forma, usando somente um decorador do FastAPI, podemos fazer com que uma determinada função seja acessível pela rede:
fast_zero/app.py | |
---|---|
- Importando da biblioteca fastapi o objeto FastAPI
- Iniciando uma aplicação FastAPI
- Definindo um endpoint com o endereço
/
acessível pelo método HTTPGET
- Função que será executada quando o endereço
/
for acessado por um cliente - Os dados que serão retornados pelo endereço quando for chamado
A linha em destaque @app.get('/')
expõem a nossa função para ser servida pelo FastAPI. Dizendo que quando um cliente acessar o nosso endereço de rede no caminho /
, usando o método HTTP GET2, a função será executada. Desta maneira, temos todo o código necessário para criar nossa primeira aplicação web com FastAPI.
Antes de iniciarmos nossa aplicação, temos que fazer um passo importante, habilitar o ambiente virtual, para que o python consiga enxergar nossas dependências instaladas. O poetry tem um comando específico para isso:
Agora com o ambiente virtual ativo, podemos iniciar nosso servidor FastAPI para iniciar nossa aplicação:
Esse comando diz ao FastAPI para iniciar o servidor de desenvolvimento (dev
) usando o arquivo fast_zero/app.py
Executar de fora do ambiente virtual
Também é possível executar os comandos sem entrar no shell do ambiente virtual. É mais verboso, mas funciona bem:
Isso evita rodar a combinação de comandos:
A resposta do comando no terminal deve ser parecida com essa:
INFO Using path fast_zero/app.py
INFO Resolved absolute path /home/dunossauro/git/fastapi-do-zero/codigo_das_aulas/01/fast_zero/app.py
INFO Searching for package file structure from directories with __init__.py files
INFO Importing from /home/dunossauro/git/fastapi-do-zero/codigo_das_aulas/01
╭─ Python package file structure ─╮
│ │
│ 📁 fast_zero │
│ ├── 🐍 __init__.py │
│ └── 🐍 app.py │
│ │
╰─────────────────────────────────╯
INFO Importing module fast_zero.app
INFO Found importable FastAPI app
╭──── Importable FastAPI app ─────╮
│ │
│ from fast_zero.app import app │
│ │
╰─────────────────────────────────╯
INFO Using import string fast_zero.app:app
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/dunossauro/git/fastapi-do-zero/codigo_das_aulas/01']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [893203] using WatchFiles
INFO: Started server process [893207]
INFO: Waiting for application startup.
INFO: Application startup complete.
A mensagem de resposta do CLI: serving: http://127.0.0.1:8000
tem uma informação bastante importante.
- Ela nos diz qual o protocolo que ele está servido
HTTP
, o protocolo padrão da web; - O endereço de rede (IP) no qual ele está escutando
127.0.0.1
, endereço especial (loopback) que aponta para a nossa própria máquina; - A porta
:8000
, a qual é a porta da nossa máquina que está reservada para nossa aplicação.
Agora, com o servidor inicializado, podemos usar um cliente para acessar o endereço http://127.0.0.1:8000.
O cliente mais tradicional da web é o navegador, podemos digitar o endereço na barra de navegação e se tudo ocorreu corretamente, você deve ver a mensagem "Olá Mundo!" em formato JSON.
Para parar a execução do fastapi no shell, você pode digitar Ctrl+C e a mensagem
Shutting down
aparecerá mostrando que o servidor foi finalizado.
Diferentes clientes para nossa aplicação
Caso exista uma curiosidade sobre outros clientes HTTP que não o browser, podemos usar aplicações de linha de comando como tradicional curl:
Ou o meu cliente HTTP preferido (escrito em python), o HTTPie:
http 127.0.0.1:8000
HTTP/1.1 200 OK
content-length: 25
content-type: application/json
date: Thu, 11 Jan 2024 11:46:32 GMT
server: uvicorn
{
"message": "Olá Mundo!"
}
Existem até mesmo aplicações gráficas de código aberto pensadas para serem clientes HTTP para APIs. Como o hoppscotch:
Ou como o Bruno:
Uvicorn
O FastAPI é ótimo para criar APIs, mas não pode disponibilizá-las na rede sozinho. Embora o FastAPI tenha uma aplicação de terminal que facilita a execução. Para podermos acessar essas APIs por um navegador ou de outras aplicações clientes, é necessário um servidor. É aí que o Uvicorn entra em cena. Ele atua como esse servidor, disponibilizando a API do FastAPI em rede. Isso permite que a API seja acessada de outros dispositivos ou programas.
Como notamos na resposta do comando fastapi dev fast_zero/app.py
:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [893203] using WatchFiles
INFO: Started server process [893207]
INFO: Waiting for application startup.
INFO: Application startup complete.
Sempre que usarmos o fastapi para inicializar a aplicação no shell, ele faz uma chamada interna para inicializar o uvicorn. Por esse motivo ele aparece nas respostas HTTP e também na execução do comando.
Você poderia chamar a aplicação diretamente pelo Uvicorn também
Esse comando diz ao uvicorn o seguinte: na pasta fast_zero existe um arquivo chamado app. Dentro desse arquivo, temos uma aplicação para ser servida com o nome de app. O comando é composto por uvicorn pasta.arquivo:variável.A resposta do comando no terminal deve ser parecida com essa:
Instalando as ferramentas de desenvolvimento
As escolhas de ferramentas de desenvolvimento, de forma geral, são escolhas bem particulares. Não costumam ser consensuais nem mesmo em times de desenvolvimento. Dito isso, selecionei algumas ferramentas que gosto de usar e alinhadas com a utilidade que elas apresentam no desenvolvimento do projeto.
As ferramentas escolhidas são:
- taskipy: ferramenta usada para criação de comandos. Como executar a aplicação, rodar os testes, etc.
- pytest: ferramenta para escrever e executar testes
- ruff: Uma ferramenta que tem duas funções no nosso código:
- Um analisador estático de código (um linter), para dizer se não estamos infringido alguma boa prática de programação;
- Um formatador de código. Para seguirmos um estilo único de código. Vamos nos basear na PEP-8.
Para instalar essas ferramentas que usaremos em desenvolvimento, podemos usar um grupo (--group dev
) do poetry focado nelas, para não serem instaladas quando nossa aplicação estiver em produção:
Configurando as ferramentas de desenvolvimento
Após a instalação das ferramentas de desenvolvimento, precisamos definir as configurações de cada uma individualmente no arquivo pyproject.toml
.
Ruff
O Ruff é uma ferramenta moderna em python, escrita em rust, compatível2 com os projetos de análise estática escritos e mantidos originalmente pela comunidade no projeto PYCQA3 e tem duas funções principais:
- Analisar o código de forma estática (Linter): Efetuar a verificação se estamos programando de acordo com boas práticas do python.
- Formatar o código (Formatter): Efetuar a verificação do código para padronizar um estilo de código pré-definido.
Para configurar o ruff montamos a configuração em 3 tabelas distintas no arquivo pyproject.toml
. Uma para as configurações globais, uma para o linter e uma para o formatador.
Configuração global
Na configuração global do Ruff queremos alterar somente duas coisas. O comprimento de linha para 79 caracteres (conforme sugerido na PEP-8) e em seguida, informaremos que o diretório de migrações de banco de dados será ignorado na checagem e na formatação:
Nota sobre "migrations"
Nessa fase de configuração, excluiremos a pasta migrations
, isso pode não fazer muito sentido nesse momento. Contudo, quando iniciarmos o trabalho com o banco de dados, a ferramenta Alembic
faz geração de código automático. Por serem códigos gerados automaticamente, não queremos alterar a configuração feita por ela.
Linter
Durante a análise estática do código, queremos buscar por coisas específicas. No Ruff, precisamos dizer exatamente o que ele deve analisar. Isso é feito por códigos. Usaremos estes:
I
(Isort): Checagem de ordenação de imports em ordem alfabéticaF
(Pyflakes): Procura por alguns erros em relação a boas práticas de códigoE
(Erros pycodestyle): Erros de estilo de códigoW
(Avisos pycodestyle): Avisos de coisas não recomendadas no estilo de códigoPL
(Pylint): Como oF
, também procura por erros em relação a boas práticas de códigoPT
(flake8-pytest): Checagem de boas práticas do Pytest
Para mais informações sobre a configuração e sobre os códigos do ruff e dos projetos do PyCQA, você pode checar a documentação do ruff ou as documentações originais dos projetos PyQCA.
Formatter
A formatação do Ruff praticamente não precisa ser alterada. Pois ele vai seguir as boas práticas e usar a configuração global de 79
caracteres por linha. A única alteração que farei é o uso de aspas simples '
no lugar de aspas duplas "
:
Lembrando que a opção de usar aspas simples é totalmente pessoal, você pode usar aspas duplas se quiser.
pytest
O Pytest é uma framework de testes, que usaremos para escrever e executar nossos testes. O configuraremos para reconhecer o caminho base para execução dos testes na raiz do projeto .
:
Na segunda linha dizemos para o pytest adicionar a opção no:warnings
. Para ter uma visualização mais limpa dos testes, caso alguma biblioteca exiba uma mensagem de warning, isso será suprimido pelo pytest.
Taskipy
A ideia do Taskipy é ser um executor de tarefas (task runner) complementar em nossa aplicação. No lugar de ter que lembrar comandos como o do fastapi, que vimos na execução da aplicação, que tal substituir ele simplesmente por task run
?
Isso funcionaria para qualquer comando complicado em nossa aplicação. Simplificando as chamadas e também para não termos que lembrar de como executar todos os comandos de cabeça.
Alguns comandos que criaremos agora no início:
pyproject.toml | |
---|---|
Os comandos definidos fazem o seguinte:
lint
: Faz a checagem de boas práticas do código pythonpre_format
: Faz algumas correções de boas práticas automaticamenteformat
: Executa a formatação do código em relação as convenções de estilo de códigorun
: executa o servidor de desenvolvimento do FastAPIpre_test
: executa a camada de lint antes de executar os testestest
: executa os testes com pytest de forma verbosa (-vv) e adiciona nosso código como base de coberturapost_test
: gera um report de cobertura após os testes
Para executar um comando, é bem mais simples, precisando somente passar a palavra task <comando>
.
Comandos com prefixo pre
e pos
Todos os comandos do taskipy que apresentam prefixos como pre_commando
ou pos_commando
não precisam ser executados diretamente. Por exemplo, se executarmos o comando task test
ele executará o comando pre_test
e caso tudo ocorra bem, sem erros, ele executará o test
, caso não aconteçam erros, o pos_test
será executado.
Nada impede que os comandos com prefixos sejam executados diretamente, mas eles são montados para serem executados em cadeia.
Caso precise ver o arquivo de configuração por completo
O meu está exatamente assim:
Um ponto importante é que as versões dos pacotes podem variar dependendo da data em que você fizer a instalação dos pacotes. Esse arquivo é somente um exemplo.
Os efeitos dessas configurações de desenvolvimento
Caso você tenha copiado o código que usamos para definir fast_zero/app.py
, pode testar os comandos que criamos para o taskipy
:
Dessa forma, veremos que cometemos algumas infrações na formatação da PEP-8. O ruff nos informará que deveríamos ter adicionado duas linhas antes de uma definição de função:
fast_zero/app.py:5:1: E302 [*] Expected 2 blank lines, found 1
|
3 | app = FastAPI()
4 |
5 | @app.get('/')
| ^ E302
6 | def read_root():
7 | return {'message': 'Olá Mundo!'}
|
= help: Add missing blank line(s)
Found 1 error.
[*] 1 fixable with the `--fix` option.
Para corrigir isso, podemos usar o nosso comando de formatação de código:
Introdução ao Pytest: Testando o "Hello, World!"
Antes de entendermos a dinâmica dos testes, precisamos entender o efeito que eles têm no nosso código. Podemos começar analisando a cobertura (o quanto do nosso código está sendo efetivamente testado). Vamos executar os testes:
Teremos uma resposta como essa:
=========================== test session starts ===========================
platform linux -- Python 3.11.7, pytest-7.4.4, pluggy-1.3.0
cachedir: .pytest_cache
rootdir: /home/dunossauro/git/fast_zero
configfile: pyproject.toml
plugins: cov-4.1.0, anyio-4.2.0
collected 0 items
---------- coverage: platform linux, python 3.11.7-final-0 -----------
Name Stmts Miss Cover
-------------------------------------------
fast_zero/__init__.py 0 0 100%
fast_zero/app.py 5 5 0%
-------------------------------------------
TOTAL 5 5 0%
As linhas no terminal são referentes ao pytest, que disse que coletou 0 itens. Nenhum teste foi executado.
Caso não tenha muita experiência com Pytest
Temos uma live de Python explicando os conceitos básicos da biblioteca
A parte importante dessa Mensagem está na tabela gerada pelo coverage
. Que diz que temos 5 linhas de código (Stmts) no arquivo fast_zero/app.py
e nenhuma delas está coberta pelos nossos testes. Como podemos ver na coluna Miss
.
Por não encontrar nenhum teste, o pytest retornou um "erro". Isso significa que nossa tarefa post_test
não foi executada. Podemos executá-la manualmente:
Isso gera um relatório de cobertura de testes em formato HTML. Podemos abrir esse arquivo em nosso navegador e entender exatamente quais linhas do código não estão sendo testadas.
Se clicarmos no arquivo fast_zero/app.py
podemos ver em vermelho as linhas que não estão sendo testadas:
Isto significa que precisamos testar todo esse arquivo.
Escrevendo o teste
Agora, escreveremos nosso primeiro teste com Pytest. Mas, antes de escrever o teste, precisamos criar um arquivo específico para eles. Na pasta tests
, vamos criar um arquivo chamado test_app.py
.
Por convenção, todos os arquivos de teste do pytest devem iniciar com um prefixo test_
.py
Para testar o código feito com FastAPI, precisamos de um cliente de teste. A grande vantagem é que o FastAPI já conta com um cliente de testes no módulo fastapi.testclient
com o objeto TestClient
, que precisa receber nosso app como parâmetro:
tests/test_app.py | |
---|---|
- Importa do módulo
testclient
o objetoTestClient
- Importa o nosso
app
definido emfast_zero
- Cria um cliente de testes usando a nossa aplicação como base
Só o fato de termos definido um cliente, já nos mostra uma cobertura bastante diferente:
task test
# parte da mensagem foi omitida
collected 0 items
---------- coverage: platform linux, python 3.11.3-final-0 -----------
Name Stmts Miss Cover
-------------------------------------------
fast_zero/__init__.py 0 0 100%
fast_zero/app.py 5 1 80%
-------------------------------------------
TOTAL 5 1 80%
Por não coletar nenhum teste, o pytest ainda retornou um "erro". Para ver a cobertura, precisaremos executar novamente o post_test
manualmente:
No navegador, podemos ver que a única linha não "testada" é aquela onde temos a lógica (o corpo) da função read_root
. As linhas de definição estão todas verdes:
No verde vemos o que foi executado quando chamamos o teste, no vermelho o que não foi.
Para resolver isso, temos que criar um teste de fato, fazendo uma chamada para nossa API usando o cliente de teste que definimos:
- No nome da função, geralmente escrevemos o que o teste de fato faz. Aqui estamos dizendo que root deve retornar o status OK e a mensagem "olá mundo". Root é o nome dado a raiz da URL. O caminho
/
, que colocamos na definição do@app.get('/')
. OK é o status que diz que a requisição aconteceu com sucesso no protocolo HTTP. - Aqui criamos o cliente de teste do nosso app
- Nesse ponto, o
client
faz uma requisição. Da mesma forma que o browser, um cliente da API. Nisso, chamamos o endereço de root, usando o método GET. - Aqui fazemos a validação do código de resposta, para saber se a resposta é referente ao código
200
, que significaOK
. Mais informações sobre esse tópico aqui. - No final, validamos se o dicionário que enviamos na função é o mesmo que recebemos quando fizemos a requisição.
- Importação da biblioteca nativa do python que abstrai os códigos de resposta e nos apresenta o status. Mais informações sobre esse tópico aqui.
Esse teste faz uma requisição GET no endpoint /
e verifica se o código de status da resposta é 200 e se o conteúdo da resposta é {'message': 'Olá Mundo!'}
.
task test
# parte da mensagem foi omitida
collected 1 item
tests/test_app.py::test_root_deve_retornar_ok_e_ola_mundo PASSED
---------- coverage: platform linux, python 3.11.3-final-0 -----------
Name Stmts Miss Cover
-------------------------------------------
fast_zero/__init__.py 0 0 100%
fast_zero/app.py 5 0 100%
-------------------------------------------
TOTAL 5 0 100%
================ 1 passed in 1.39s ================
Wrote HTML report to htmlcov/index.html
Dessa forma, temos um teste que coletou 1 item (1 teste). Esse teste foi aprovado e a cobertura não deixou de abranger nenhuma linha de código.
Como conseguimos coletar um item, o post_test
foi executado e também gerou um HTML com a cobertura atualizada.
Estrutura de um teste
Agora que escrevemos nosso primeiro teste de forma intuitiva, podemos entender o que cada passo do teste faz. Essa compreensão é vital, pois nos ajudará a escrever testes com mais confiança e eficácia. Para desvendar o método por trás da nossa abordagem, exploraremos uma estratégia conhecida como AAA, que divide o teste em três fases distintas: Arrange, Act, Assert.
Caso fazer testes ainda seja complicado para você
Temos uma live de python focada em ensinar os primeiros passos no mundo dos testes.
Para analisar todas as etapas de um teste, usaremos como exemplo este primeiro teste que escrevemos:
Com base nesse código, podemos observar as três fases:
Fase 1 - Organizar (Arrange)
Nesta primeira etapa, estamos preparando o ambiente para o teste. No exemplo, a linha com o comentário Arrange
não é o teste em si, ela monta o ambiente para que o teste possa ser executado. Estamos configurando um client
de testes para fazer a requisição ao app
.
Fase 2 - Agir (Act)
Aqui é a etapa onde acontece a ação principal do teste, que consiste em chamar o Sistema Sob Teste (SUT). No nosso caso, o SUT é a rota /
, e a ação é representada pela linha response = client.get('/')
. Estamos exercitando a rota e armazenando sua resposta na variável response
. É a fase em que o código de testes executa o código de produção que está sendo testado. Agir aqui significa interagir diretamente com a parte do sistema que queremos avaliar, para ver como ela se comporta.
Fase 3 - Afirmar (Assert)
Esta é a etapa de verificar se tudo correu como esperado. É fácil notar onde estamos fazendo a verificação, pois essa linha sempre tem a palavra reservada assert
. A verificação é booleana, ou está correta, ou não está. Por isso, um teste deve sempre incluir um assert
para verificar se o comportamento esperado está correto.
Agora que compreendemos o que cada linha de teste faz em específico, podemos nos orientar de forma clara nos testes que escreveremos no futuro. Cada uma das linhas usadas tem uma razão de estar no teste, e conhecer essa estrutura não só nos dá uma compreensão mais profunda do que estamos fazendo, mas também nos dá confiança para explorar e escrever testes mais complexos.
Criando nosso repositório no git
Antes de concluirmos a aula, precisamos executar alguns passos:
- Criar um arquivo
.gitignore
para não adicionar o ambiente virtual e outros arquivos desnecessários no versionamento de código. - Criar um novo repositório no GitHub para versionar o código.
- Subir o código que fizemos para o GitHub.
Criando o arquivo .gitignore
Vamos iniciar com a criação de um arquivo .gitignore
específico para Python. Existem diversos modelos disponíveis na internet, como os disponíveis pelo próprio GitHub, ou o gitignore.io. Uma ferramenta útil é a ignr
, feita em Python, que faz o download automático do arquivo para a nossa pasta de trabalho atual:
- O comando
pipx run
vai baixar oignr
vai executar o comando e vai desinstalar. Não existe a necessidade de termos ele instalado no sistema pois só será executado dessa vez.
O .gitignore
é importante porque ele nos ajuda a evitar que arquivos desnecessários ou sensíveis sejam enviados para o repositório. Isso inclui o ambiente virtual, arquivos de configuração pessoal, entre outros.
Criando um repositório no github
Agora, com nossos arquivos indesejados ignorados, podemos iniciar o versionamento de código usando o git
. Para criar um repositório local, usamos o comando git init .
. Para criar esse repositório no GitHub, utilizaremos o gh
, um utilitário de linha de comando que nos auxilia nesse processo:
Ao executar gh repo create
, algumas informações serão solicitadas, como o nome do repositório e se ele será público ou privado. Isso irá criar um repositório tanto localmente quanto no GitHub.
Subindo nosso código para o github
Com o repositório pronto, vamos versionar nosso código. Primeiro, adicionamos o código ao próximo commit com git add .
. Em seguida, criamos um ponto na história do projeto com git commit -m "Configuração inicial do projeto"
. Por fim, sincronizamos o repositório local com o remoto no GitHub usando git push
:
Caso seja a primeira vez que está utilizando o
git push
, talvez seja necessário configurar suas credenciais do GitHub.
Esses passos garantem que todo o código criado na aula esteja versionado e disponível para compartilhamento no GitHub.
Suplementar / Para próxima aula
Caso o mundo dos testes ainda seja um pouco nebuloso para você, recomendo que antes de partir para a próxima aula, você dê uma assistida em algumas lives de python de fora desse curso:
- Uma introdução aos testes: Como fazer? | Live de Python #232
- Pytest: Uma introdução - Live de Python #167
- Pytest Fixtures - Live de Python #168
Exercício
- Crie um repositório para acompanhar o curso e suba em alguma plataforma, como Github, gitlab, codeberg, etc. E compartilhe o link no repositório do curso para podermos aprender juntos.
Conclusão
Pronto! Agora temos um ambiente de desenvolvimento totalmente configurado para começar a trabalhar com FastAPI e já fizemos nossa primeira imersão no Desenvolvimento Orientado por Testes. Na próxima aula nos aprofundaremos na estruturação da nossa aplicação FastAPI. Até lá!
Agora que a aula acabou, é um bom momento para você relembrar alguns conceitos e fixar melhor o conteúdo respondendo ao questionário referente a ela.