A synthetic portfolio project that shows how I structure API test automation for a small business domain. It includes a FastAPI mock service, a pytest framework, a CLI, a lightweight dashboard, CI, Docker support, and sample reports.
All names, records, and workflows are fictional. This is a demo project, not a production system and not based on a real employer or client.
API teams need fast feedback that endpoints still honor response contracts, CRUD behavior, and core business rules before changes reach users. This project models that problem with a simple customer, order, and billing API, then demonstrates how an automation framework can validate it locally and in CI without requiring a deployed environment.
- Python 3.11+ API automation with
pytest, reusable fixtures, and a smallAPIClient - Contract checks with JSON Schema
- Positive, negative, and business-rule test coverage across customer, order, and billing flows
- FastAPI endpoints using Pydantic v2 request/response models
- In-process tests via FastAPI
TestClient; no live server required forpytest - Optional live-server workflows through the CLI, Swagger UI, and web dashboard
- CI validation with Ruff, pytest, coverage, and an uploaded HTML report
Requirements: Python 3.11+
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install -r requirements-dev.txtRun the automated tests:
pytest -vStart the demo API and dashboard:
uvicorn app.main:app --reloadOpen:
- Dashboard: http://localhost:8000/ui/
- Swagger UI: http://localhost:8000/docs
- Health check: http://localhost:8000/health
Use these to evaluate the repo quickly:
# CI-style local validation
ruff check .
pytest --html=reports/report.html --self-contained-html --cov=app --cov-report=term-missing -v
# CLI demo, with the server running
python cli.py health
python cli.py customers list
python cli.py orders list
python cli.py billing invoicesThe tests run in-process by default. To point tests or the CLI at a live server, set:
API_BASE_URL=http://localhost:8000graph TD
CI["GitHub Actions CI"]
TESTS["pytest suite"]
CLIENT["APIClient"]
API["FastAPI mock API"]
DATA["In-memory seed data"]
UI["Web dashboard"]
CLI["Typer CLI"]
CI --> TESTS
TESTS --> CLIENT
CLIENT --> API
UI --> API
CLI --> API
API --> DATA
The repository has three main layers:
| Layer | Purpose |
|---|---|
app/ |
FastAPI mock service for customers, orders, billing, health, and UI routes |
tests/ |
pytest automation framework with fixtures, schemas, reusable client, and domain tests |
cli.py |
Typer/Rich commands for health checks, CRUD demos, test runs, and server startup |
State is intentionally stored in memory under app/data/seed_data.py. That keeps the demo deterministic and easy to reset. Persistence, authentication, authorization, and load testing are intentionally out of scope.
qa-api-automation-framework/
+-- app/
| +-- main.py # FastAPI app, router registration, health endpoint
| +-- models.py # Pydantic v2 domain and request models
| +-- data/seed_data.py # Synthetic in-memory seed records
| +-- routers/ # Customer, order, and billing API routers
| +-- ui/ # Jinja2 dashboard routes and templates
+-- tests/
| +-- api_client.py # Reusable requests-compatible API client
| +-- conftest.py # Fixtures for client, test data, and schemas
| +-- schemas/ # JSON Schema contract files
| +-- test_*.py # Health, CRUD, contract, negative, and business-rule tests
+-- testdata/ # JSON payloads used by tests
+-- docs/
| +-- architecture.md
| +-- test-strategy.md
| +-- sample-report.md
| +-- screenshots/
+-- .github/workflows/ci.yml # Ruff + pytest + coverage + HTML report artifact
+-- cli.py
+-- Dockerfile
+-- docker-compose.yml
+-- requirements.txt
+-- requirements-dev.txt
| Domain | Endpoints |
|---|---|
| Health | GET /health |
| Customers | GET /customers, GET /customers/{id}, POST /customers, PATCH /customers/{id}, DELETE /customers/{id} |
| Orders | GET /orders, GET /orders/{id}, POST /orders, PATCH /orders/{id}, DELETE /orders/{id} |
| Billing | GET /billing/invoices, GET /billing/invoices/{id}, POST /billing/events, PATCH /billing/invoices/{id}, DELETE /billing/invoices/{id} |
The suite covers:
- Smoke and health checks
- Happy-path CRUD flows for all three domains
- JSON Schema response contract validation
- Parametrized data checks
- Negative paths for missing resources and invalid payloads
- Business rules, including duplicate customer emails, closed customers blocking new orders, and billing events requiring valid customer/order relationships
Example output:
collected 51 items
tests/test_billing.py::test_list_invoices_returns_list PASSED
tests/test_customers.py::test_create_valid_customer PASSED
tests/test_health.py::test_health_returns_200 PASSED
tests/test_negative_scenarios.py::test_create_order_for_closed_customer_fails PASSED
tests/test_orders.py::test_create_order_for_active_customer PASSED
...
51 passed
See docs/sample-report.md for a longer sample run with coverage output.
Start the API first with uvicorn app.main:app --reload, then run:
python cli.py health
python cli.py customers list
python cli.py customers get CUST-001
python cli.py customers create --first-name Alice --last-name Smith --email alice@example.com
python cli.py orders list
python cli.py orders create --customer-id CUST-001 --order-type NEW --amount 150
python cli.py billing invoices
python cli.py billing event --customer-id CUST-001 --order-id ORD-001 --amount 250
python cli.py test --cov
python cli.py serve --port 9000 --no-reloadWith the API running, visit http://localhost:8000/ui/.
| Page | Purpose |
|---|---|
| About | Project framing, architecture, testing approach, and API summary |
| Dashboard | Counts, recent orders, and latest test run summary |
| Customers / Orders / Billing | Basic CRUD demos for the synthetic domain |
| Test Runner | Browser-triggered pytest run with output |
| Test History | Stored run summaries from the dashboard test runner |
| Test Details | Test intent, HTTP action, assertions, and source view |
GitHub Actions runs on pushes and pull requests:
- Install
requirements-dev.txt - Run
ruff check . - Run pytest with coverage and an HTML report
- Upload
reports/report.htmlas an artifact
Generated files such as .coverage, htmlcov/, caches, and local HTML reports are ignored. docs/sample-report.md keeps a text example in version control for quick review.
docker-compose up --buildThe API will be available at http://localhost:8000.
| Document | Description |
|---|---|
| docs/architecture.md | Layer breakdown, data flow, and CI flow |
| docs/test-strategy.md | Test categories, business rules, and out-of-scope items |
| docs/sample-report.md | Sample pytest and coverage output |
This project keeps the system small so the automation design is easy to evaluate. It does not claim production readiness. The following are reasonable next enhancements, but are outside the current scope:
- Authentication and authorization flows
- SQLite or Postgres persistence
- Async test variants
- Property-based testing
- Load or performance testing
