diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b7af428..5a54b70 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,9 +2,9 @@ name: API Tests on: push: - branches: [main] + branches: [master] pull_request: - branches: [main] + branches: [master] schedule: - cron: '0 6 * * *' @@ -37,6 +37,14 @@ jobs: - name: Rodar testes run: pytest + - name: Enviar cobertura para o Codecov + if: always() + uses: codecov/codecov-action@v7 + with: + files: reports/coverage.xml + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false + - name: Publicar relatório HTML if: always() uses: actions/upload-artifact@v7 diff --git a/.gitignore b/.gitignore index 9993f66..d82cc2a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ venv/ # Test reports reports/ .pytest_cache/ +.coverage +.coverage.* +htmlcov/ # Type checking .mypy_cache/ @@ -25,3 +28,6 @@ reports/ # VS Code .vscode/ + +# Log de decisões do projeto (uso pessoal, não é material público) +Decisões projeto.md diff --git a/README.md b/README.md index f6dfb8d..cf32e18 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # 🔌 QA API + pytest-bdd - restful-booker [![API Tests](https://github.com/ThomasTDS/qa-api-python/actions/workflows/tests.yml/badge.svg)](https://github.com/ThomasTDS/qa-api-python/actions/workflows/tests.yml) +[![codecov](https://codecov.io/gh/ThomasTDS/qa-api-python/branch/master/graph/badge.svg)](https://codecov.io/gh/ThomasTDS/qa-api-python) ## Descrição @@ -25,7 +26,7 @@ qa-api-python/ ├── steps/ # Implementação dos steps do pytest-bdd ├── api/ # API Clients (AuthApiClient, BookingApiClient) ├── models/ # Schemas Pydantic e tipos derivados (shape dos dados da API) -├── reports/ # Relatório HTML gerado a cada execução (não versionado) +├── reports/ # Relatório HTML e cobertura (coverage.xml) gerados a cada execução (não versionado) ├── conftest.py # Fixtures (contexto por cenário) e captura de evidência de falha ├── pyproject.toml # Dependências, scripts e configuração (pytest, ruff, mypy) ├── .pre-commit-config.yaml # Hook de pre-commit (ruff) @@ -75,6 +76,8 @@ ruff format . # formatação, aplica as correções O CI roda `mypy`, `ruff check` e `ruff format --check` antes dos testes, então mudanças com problema de tipo ou estilo falham rápido, sem gastar tempo batendo na API pública. +Cada execução de `pytest` já gera cobertura de `api/` e `models/` (código dos API Clients e dos schemas), impressa no terminal e também em `reports/coverage.xml` (não versionado). No CI esse arquivo é enviado para o [Codecov](https://codecov.io/gh/ThomasTDS/qa-api-python), que mantém o histórico e mostra o badge no topo deste README. + Um hook de pre-commit (framework `pre-commit`, instalado via `pre-commit install` após o `pip install`) roda `ruff --fix` e `ruff format` nos arquivos staged antes de cada commit, então a maioria dos problemas de lint/formatação já é corrigida localmente antes de chegar no CI. ### Rodar contra outra URL @@ -116,9 +119,10 @@ Autenticação: `POST /auth` com `{ "username": "admin", "password": "password12 - Validação de schema com [Pydantic](https://docs.pydantic.dev/): as respostas da API são validadas em tempo de execução contra os modelos em `models/booking.py` (fonte única de verdade), não só tipadas por anotação — se a API mudar o formato de uma resposta, o teste falha com uma mensagem clara em vez de passar silenciosamente ou quebrar mais adiante. - Cobertura de autenticação, CRUD completo, PUT vs. PATCH, e casos de acesso não autorizado (403). - Relatório HTML automatizado a cada execução (`reports/report.html`, via `pytest-html`). +- Cobertura de código (`pytest-cov`) de `api/` e `models/` em cada execução, acompanhada no [Codecov](https://codecov.io/gh/ThomasTDS/qa-api-python). - Limpeza automática: a fixture `context` remove o booking criado no cenário (via token próprio de limpeza) ao final de cada teste, evitando acúmulo de dados na API pública. - Retry automático (`pytest-rerunfailures`, `--reruns 1`): um cenário que falha roda uma segunda vez antes de ser reportado como falha, amortecendo instabilidade transitória da API pública de demonstração. -- Integração contínua via GitHub Actions: os testes rodam automaticamente a cada push e pull request para `main`, e também diariamente às 06:00 UTC (ver [.github/workflows/tests.yml](.github/workflows/tests.yml)) para detectar quebras causadas pela própria API pública, com o relatório HTML publicado como artifact do workflow. +- Integração contínua via GitHub Actions: os testes rodam automaticamente a cada push e pull request para `master`, e também diariamente às 06:00 UTC (ver [.github/workflows/tests.yml](.github/workflows/tests.yml)) para detectar quebras causadas pela própria API pública, com o relatório HTML publicado como artifact do workflow. - Dependências atualizadas automaticamente pelo Dependabot (pip e GitHub Actions, semanal — ver [.github/dependabot.yml](.github/dependabot.yml)). PRs de patch/minor com CI verde são mergeados automaticamente ([.github/workflows/dependabot-auto-merge.yml](.github/workflows/dependabot-auto-merge.yml)); bumps de major exigem revisão manual. - Rastreabilidade de QA: matriz de test cases em [docs/test-cases.md](docs/test-cases.md), com tags `@TC-XXX` em cada `Scenario` e um subconjunto `@smoke` (`pytest -m smoke`) cobrindo os fluxos ponta-a-ponta mais críticos. Bugs reais encontrados são documentados como GitHub Issues usando o template em [.github/ISSUE_TEMPLATE/bug_report.md](.github/ISSUE_TEMPLATE/bug_report.md). @@ -126,7 +130,7 @@ Autenticação: `POST /auth` com `{ "username": "admin", "password": "password12 ### Fluxo de Trabalho -A branch `main` é protegida: toda mudança passa por Pull Request, e o merge só é liberado depois que o check de CI (`test`) passa. Fluxo padrão: +A branch `master` é protegida: toda mudança passa por Pull Request, e o merge só é liberado depois que o check de CI (`test`) passa. Fluxo padrão: ``` git checkout -b minha-branch diff --git a/api/booking_api_client.py b/api/booking_api_client.py index d1e678f..921572a 100644 --- a/api/booking_api_client.py +++ b/api/booking_api_client.py @@ -48,6 +48,3 @@ def partial_update_booking(self, booking_id: int, partial_booking: dict[str, Any def delete_booking(self, booking_id: int, token: str) -> requests.Response: return self.session.delete(f"{self.base_url}/booking/{booking_id}", cookies={"token": token}) - - def exemplo_com_bug(self, booking_id: int) -> None: - self.get_booking(booking_id) diff --git a/pyproject.toml b/pyproject.toml index ce28281..8e828b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ dev = [ "pytest-bdd>=8.1", "pytest-rerunfailures>=15.0", "pytest-html>=4.1", + "pytest-cov>=6.0", "ruff>=0.14", "mypy>=1.14", "types-requests>=2.32", @@ -29,7 +30,7 @@ include = ["api*", "models*"] [tool.pytest.ini_options] testpaths = ["steps"] -addopts = "--reruns 1 --html=reports/report.html --self-contained-html" +addopts = "--reruns 1 --html=reports/report.html --self-contained-html --cov=api --cov=models --cov-report=term-missing --cov-report=xml:reports/coverage.xml" markers = [ "smoke: fluxos ponta-a-ponta mais críticos (equivalente a npm run test:smoke)", "TC-001: id do test case, ver docs/test-cases.md", @@ -59,3 +60,9 @@ python_version = "3.12" strict = true ignore_missing_imports = true exclude = ["reports"] + +[tool.coverage.run] +branch = true + +[tool.coverage.report] +exclude_lines = ["pragma: no cover", "if TYPE_CHECKING:"]