Prompt
How do I set up reusable parameterized API tests?
Latest observation
A good way to set up reusable parameterized API tests is to separate:
- Test data
- API call logic
- Assertions
- Test runner/parameterization
That lets you run the same test against many inputs/expected outcomes without duplicating code.
General pattern
1) Define a test case model
Create a structure for each case:
- input parameters
- expected status code
- expected response fields
- optional auth headers / query params / payloads
Example shape:
{
"name": "valid user id",
"method": "GET",
"url": "/users/123",
"expectedStatus": 200,
"expectedBody": { "id": 123, "active": true }
}
2) Put test cases in a data file
Store cases in JSON/YAML/CSV or inline in code.
Benefits:
- easy to add cases
- non-devs can review/update scenarios
- same test logic applies everywhere
3) Write one reusable request helper
Make a function that sends the API request given a case definition.
Example helper responsibilities:
- build URL
- attach headers/token
- serialize payload
- send request
- return response
4) Write a parameterized test
Use your framework’s data-driven testing feature:
- pytest:
@pytest.mark.parametrize - JUnit 5:
@ParameterizedTest - NUnit:
[TestCase],[TestCaseSource] - Jest/Mocha: loop over cases or use
test.each
Example in Python with pytest
test_cases.json
[
{
"name": "valid user",
"method": "GET",
"url": "/users/1",
"expected_status": 200,
"expected_contains": { "id": 1 }
},
{
"name": "missing user",
"method": "GET",
"url": "/users/99999",
"expected_status": 404
}
]
test_api.py
import json
import requests
import pytest
BASE_URL = "https://api.example.com"
def load_cases():
with open("test_cases.json") as f:
return json.load(f)
def send_request(case):
url = BASE_URL + case["url"]
method = case["method"].lower()
response = requests.request(method, url)
return response
@pytest.mark.parametrize("case", load_cases(), ids=lambda c: c["name"])
def test_api_case(case):
response = send_request(case)
assert response.status_code == case["expected_status"]
if "expected_contains" in case:
body = response.json()
for key, value in case["expected_contains"].items():
assert body[key] == value
Best practices
Keep test logic DRY
Centralize:
- authentication
- request building
- JSON parsing
- common assertions
Make cases readable
Use names like:
valid_admin_userinvalid_token_returns_401empty_payload_returns_400
Assert only what matters
Avoid brittle tests that compare entire response bodies unless necessary. Focus on:
- status code
- key fields
- schema
- important business rules
Add schema validation
For APIs, it’s often useful to validate against a schema in addition to field assertions.
Separate positive and negative cases
Group:
- success scenarios
- validation failures
- auth/permission failures
- edge cases
Use environment config
Don’t hardcode:
- base URL
- secrets
- tokens
Use environment variables or config files.
If you want even more reuse
You can build a small abstraction like:
def run_api_test(case, client):
response = client.request(case["method"], case["url"], json=case.get("payload"))
assert response.status_code == case["expected_status"]
Then multiple test files can call the same helper for different endpoints.
Recommended structure
tests/
api/
test_users.py
test_orders.py
data/
users_cases.json
orders_cases.json
helpers/
api_client.py
assertions.py
If you tell me your language/framework (pytest, JUnit, Postman/Newman, Cypress, Playwright, etc.), I can show a version tailored to it.