API & Data
API Testing
HTTP fundamentals, what to validate, test-case design, authentication, schema and contract testing, and C# / Playwright implementation — with rapid-fire Q&A.
Now playing: API Testing
API Testing
Two-host episode · 13:25
Now playing: API Testing
API Testing
Mock interview · 7:58
01Core HTTP
Methods & semantics
- GET — retrieve, no body, idempotent, cacheable.
- POST — create, not idempotent (two calls = two resources).
- PUT — full replace/update, idempotent.
- PATCH — partial update, not necessarily idempotent.
- DELETE — remove, idempotent.
02Status Codes — Know These Cold
| Code | Meaning & when |
|---|---|
| 200 | OK — successful GET / general success. |
| 201 | Created — POST succeeded; often returns a Location header. |
| 202 | Accepted — request queued for async processing. |
| 204 | No Content — success with empty body (DELETE / PUT). |
| 301/302 | Permanent / temporary redirect. |
| 304 | Not Modified — caching / conditional requests. |
| 400 | Bad Request — malformed syntax / payload. |
| 401 | Unauthorized — not authenticated (who are you?). |
| 403 | Forbidden — authenticated but no permission (you can't do this). |
| 404 | Not Found. |
| 405 | Method Not Allowed. |
| 409 | Conflict — e.g. duplicate creation. |
| 422 | Unprocessable Entity — semantic / validation failure. |
| 429 | Too Many Requests — rate limited. |
| 500 | Internal Server Error. |
| 502/503/504 | Bad Gateway / Service Unavailable / Gateway Timeout. |
401 vs 403 is a classic — 401 means the server doesn't know who you are; 403 means it knows you and you're still not allowed.
03What You Actually Validate
For any API response, check beyond just the status line:
- Status code — correct for the scenario.
- Response body — correct values and correct data types.
- Schema — structure matches the contract (JSON Schema: required fields, shape, types).
- Headers —
Content-Type, auth, caching, rate-limit headers. - Response time — within the SLA.
- Side effects — did the POST actually persist? Verify with a follow-up GET.
04Test Case Design
Valid inputs produce the expected success response and the expected persisted state.
- Missing required fields → 400 / 422.
- Invalid data types or malformed JSON → 400.
- Missing / invalid / expired auth token → 401.
- Insufficient permissions → 403.
- Non-existent resource → 404.
- Duplicate creation → 409.
- Boundary values — empty string, max length, zero, negatives, oversized payloads.
Pagination, filtering / sorting parameters, rate limiting (429), concurrency, and idempotency keys.
05Authentication
- API Key — static key in a header or query param.
- Bearer token / JWT —
Authorization: Bearer <token>. A JWT isheader.payload.signature; the payload carries claims (exp,sub, roles). - OAuth 2.0 — token-exchange flow; in tests you typically grab a token via client-credentials or password grant in setup, then reuse it.
- Basic Auth — base64
user:pass, rarely used now.
06Schema & Contract
JSON Schema validation
Assert the shape — required fields present, correct types, structure intact — not just individual values. More robust and maintainable than field-by-field assertions when the payload is large.
Contract testing (Pact)
Consumer and provider agree on a contract; it catches breaking changes between services without a full integration environment. It's consumer-driven.
Schema validation checks the shape of one response. Contract testing verifies the agreement between two services stays intact.
07API vs UI Testing
- API tests are faster, more stable, and less flaky — no rendering, no waiting on elements.
- They exercise business logic and data integrity directly.
- They sit in the middle of the test pyramid — many fast unit tests at the base, a healthy API/integration layer in the middle, few UI/E2E tests at the top.
- They catch issues earlier and pinpoint failures more precisely than UI tests.
08Mocking & Test Data
- Mock / stub external dependencies (WireMock, mock servers) to isolate the service under test and control responses.
- Setup / teardown — create preconditions via API, clean up after; keep each test independent and idempotent.
- Environment management — base URLs, secrets, and tokens configured per environment (dev / staging).
09C# Implementation
RestSharp
var client = new RestClient("https://api.example.com");
var request = new RestRequest("/users/1", Method.Get);
request.AddHeader("Authorization", $"Bearer {token}");
var response = await client.ExecuteAsync(request);
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var user = JsonConvert.DeserializeObject<User>(response.Content);
Assert.That(user.Id, Is.EqualTo(1));
Playwright API testing
var request = await playwright.APIRequest.NewContextAsync(new()
{
BaseURL = "https://api.example.com",
ExtraHTTPHeaders = new Dictionary<string, string>
{
["Authorization"] = $"Bearer {token}"
}
});
var response = await request.GetAsync("/users/1");
Assert.That(response.Status, Is.EqualTo(200));
var body = await response.JsonAsync();
APIRequestContext lets you mix API setup with UI tests in one framework — e.g. create test data via API, then verify it in the UI. Clean way to seed state without clicking through the app.
Suite structure to raise
- A base / client wrapper class for the HTTP client and auth.
- POCO models for request / response deserialization.
[SetUp]/[OneTimeSetUp]for token acquisition (NUnit), or fixtures (xUnit).- Data-driven tests (
[TestCase],[TestCaseSource]) for negative cases. - Separate config per environment.
10Rapid-Fire Q&A
Reveal each answer to self-check, then test yourself with the quiz.
PUT vs PATCH?
PUT replaces the whole resource (missing fields get reset); PATCH updates only the fields sent. PUT is idempotent; PATCH usually is but not guaranteed.
401 vs 403?
401 = not authenticated, server doesn't know you. 403 = authenticated but not permitted for this resource.
How do you validate a response beyond status code?
Body values & types, schema/structure, headers, response time, and side effects (verify a POST persisted with a follow-up GET).
How do you test an authenticated endpoint?
Acquire a token in setup, attach Authorization: Bearer, reuse/refresh it. Also test no-token → 401, expired → 401, wrong role → 403.
What's idempotency?
A request that, made once or many times, leaves the server in the same state. GET/PUT/DELETE yes, POST no. Refers to state, not response.
How do you handle dynamic / changing response data?
Don't hard-assert volatile fields (IDs, timestamps). Assert type/format or capture generated values at runtime and reuse them; validate format or a range for timestamps.
How do you keep API tests independent?
Each test sets up its own data and cleans up; no shared mutable state, no order dependency; unique/generated data (GUIDs) for safe parallel runs.
Where do API tests fit in the test pyramid?
The middle layer — above unit, below UI — the best balance of coverage, speed, and stability.