A Loja do Bairro vai registrar vendas pelo sistema, e cada venda baixa o estoque. Se a venda for registrada sem a baixa (ou a baixa sem a venda), o estoque da loja fica errado. Antes de aceitar a entrega, a loja exige provas automáticas de que isso não acontece. Hoje vocês testam o sistema funcionando em conjunto: banco de dados, rotas e login.
🖥️ Atividades
1. Testar a venda no banco real
Antes de começar, faça uma cópia do banco:cp gestao_produtos.db gestao_produtos.bak (no Windows: copy gestao_produtos.db gestao_produtos.bak).
Crie o arquivo tests/test_rascunho_banco_real.py.
Escreva um teste que confirme: uma venda de 100 unidades de um produto com 3 em estoque não altera o estoque nem registra a venda.
Como o banco real não tem um produto com exatamente 3 em estoque, o próprio teste deve cadastrar um produto de teste com estoque 3 antes de tentar a venda.
Execute o teste duas vezes e olhe o banco (/produtos) depois de cada execução.
Questões
Por que o teste mexeu nos dados do sistema?
O que mudou na segunda execução?
Como preparar um cenário que seja sempre igual, sem sujar o banco do sistema?
2. Conhecer as fixtures
Abra o arquivo tests/conftest.py, entregue pelo docente, e leia os comentários.
Identifique: o que cada fixture prepara e o que acontece com o banco temporário depois de cada teste.
Apague o arquivo tests/test_rascunho_banco_real.py e restaure a cópia: cp gestao_produtos.bak gestao_produtos.db.
3. Decidir o tipo de teste de cada funcionalidade
Em docs/plano-de-teste.md, acrescente à tabela dos casos CT01 a CT06 duas colunas: tipo de teste mais adequado (manual, unitário, integração ou aceitação) e justificativa.
4. Escrever os testes de integração
Crie tests/test_integracao.py com os 4 testes obrigatórios da tabela abaixo.
Execute toda a suíte (pytest) e confira que os testes das Aulas 2 e 3 passam.
No docs/plano-de-teste.md, marque quais casos foram automatizados e em qual arquivo.
Faça o commit local.
5. Avaliação 1
Prepare a apresentação da sua dupla (veja o quadro Avaliação 1 abaixo).
Três níveis de teste
Tipo
O que testa
Exemplo no nosso sistema
Unitário
Uma função isolada
validar_produto recusa preço negativo
Integração
Várias partes funcionando juntas
Rota + sessão + banco: login e acesso à lista
Aceitação
O sistema do ponto de vista de quem o usa
”Consigo cadastrar um produto e vê-lo na lista?”
Nenhum substitui os outros. A pergunta é: para esta funcionalidade, qual tipo encontra os problemas mais importantes com menos esforço?
O que é uma fixture
Uma fixture é um cenário preparado antes de cada teste e descartado depois, como uma bancada de laboratório montada e limpa a cada experimento.
No pytest, você pede a fixture escrevendo o nome dela como parâmetro do teste:
O pytest vê o parâmetro cliente, procura a fixture com esse nome no conftest.py, prepara o cenário e entrega o resultado ao teste.
Fixtures entregues no tests/conftest.py
Fixture
O que entrega
banco_temporario
Um banco novo e descartável, com 3 categorias, 5 produtos e 1 usuário. O sistema passa a usar esse banco durante o teste
banco
Uma conexão para você consultar o banco temporário e conferir o resultado
cliente
O test client do Flask: um “navegador de mentira” que faz requisições sem abrir o servidor
cliente_logado
O mesmo cliente, já com o login feito
obter_token_csrf
Função que lê o token CSRF do formulário (usada nos desafios)
Dados do banco temporário (sempre os mesmos, a cada teste):
id
Produto
Estoque
1
Smartphone
10
2
Notebook
5
3
Sofá
3
4
Cadeira de Escritório
12
5
Camisa Polo
25
Usuário de teste:tester com a senha SenhaTeste123.
Como usar o test client e verificar erros esperados
resposta = cliente.get("/produtos") # como digitar o endereçoresposta = cliente.post("/login", data={...}) # como enviar um formulárioresposta.status_code # 200, 302 (redirecionamento), 403...resposta.headers["Location"] # para onde redirecionouresposta.get_data(as_text=True) # o HTML devolvido
Quando o erro é o resultado esperado (a venda sem estoque deve falhar), use pytest.raises: o teste passa se o erro acontecer e falha se não acontecer.
import pytestdef test_divisao_por_zero_gera_erro(): with pytest.raises(ZeroDivisionError): 1 / 0
Os 4 testes obrigatórios de tests/test_integracao.py
A função de venda é dados.registrar_venda(conn, id_produto, quantidade). Para abrir a conexão, use dados.abrir_conexao().
#
Teste
O que verificar
1
Venda com estoque suficiente
Vender 3 unidades do Smartphone: o estoque cai de 10 para 7 e a venda fica registrada
2
Venda com estoque insuficiente
Vender 100 unidades do Sofá (estoque 3): ocorre sqlite3.IntegrityError, o estoque continua 3 e nenhuma venda é registrada
3
Acesso restrito
Abrir /produtos/novosem login: a resposta é um redirecionamento (302) para o login
4
Login válido
Enviar o login correto: a resposta é um redirecionamento (302); depois, /produtos responde 200 e mostra “Smartphone”
Desafios opcionais
#
Teste
O que verificar
5
Envio sem token CSRF
Com login feito, enviar o formulário de produto sem o token: resposta 403 e nenhum produto gravado
6
Dados inválidos
Com login e token corretos, enviar preço -10: resposta 400, mensagem de preço inválido e nenhum produto gravado
Lembre-se do padrão Preparar, Executar e Verificar, e de que cada teste precisa funcionar sozinho.
Comandos úteis
pytest -v # lista cada testepytest tests/test_integracao.py # só os de integraçãopytest -k "venda" # só os testes com "venda" no nomepytest --durations=3 # mostra os 3 testes mais lentos
O login faz o hash da senha (lento de propósito), então os testes de login levam um pouco mais.
Regras de ouro
Nenhum teste da pasta tests/ pode mexer no banco real (exceto o rascunho da atividade 1, que será apagado).
Os testes não devem depender da ordem em que rodam.
Se um teste falhar, leia a mensagem completa antes de mexer no código.
Avaliação 1 (última hora da aula)
Cada dupla apresenta em cerca de 8 minutos:
O plano de teste e os 6 casos (como foram escritos e executados).
A escolha do tipo de teste para cada funcionalidade, com a justificativa.
A suíte automatizada rodando (pytest -v) e uma explicação de um teste de integração.
Uma resposta curta: se um teste passa, isso garante que não há defeitos? Por quê?
O que será observado (Indicador 1): se vocês usam metodologias diferentes de teste e escolhem cada uma conforme a funcionalidade: casos manuais bem escritos, adequação do tipo de teste a cada funcionalidade e testes unitários e de integração funcionando.
O que você vai entregar
tests/test_integracao.py com os 4 testes obrigatórios
docs/plano-de-teste.md atualizado: tipo de teste e justificativa por caso, e os casos automatizados marcados
O resultado do pytestsem falhas (captura de tela ou saída copiada)
Um commit local com mensagem clara
Para refletir
Qual funcionalidade vale mais a pena testar à mão, e qual vale mais a pena automatizar? Por quê?
O que aconteceria com a suíte se cada teste usasse o banco real?