Write Tests for an API Client
You're writing tests for an ApiClient class — a reusable wrapper around fetch that handles errors, parses JSON, and retries transient server failures.
The class is in src/ApiClient.ts — read-only. Your job is in tests/ApiClient.test.ts.
The judge will swap in four buggy variants of the class. Your tests must:
- All pass against the correct implementation.
- Catch each bug — at least one test must fail against each buggy variant.
You can't make real network calls in the judge. Replace globalThis.fetch with a vi.fn() mock before each test.
- Use Vitest (
describe,it,expect,vi) — already available in the runtime. - Import
ApiClientfrom'../src/ApiClient'. - Mock
globalThis.fetchusingvi.fn().mockResolvedValue(...)— do not make real network calls. - Every test must pass against the reference implementation.
- Your suite must produce at least one failing test against each of four buggy variants: missing error handling on any non-ok response, 4xx treated as success (only 5xx throws), raw Response object returned instead of parsed JSON, and 401 triggering retries when it should throw immediately.
GET /users → 200 with body { id: 1 }Promise resolves to { id: 1 }A 200 response is parsed as JSON and returned directly.
GET /not-found → 404
Promise rejects with Error('HTTP 404')Any non-ok status that isn't retried must throw with the status code.
GET /protected → 401 (maxRetries = 2)
fetch called exactly once, then rejects with Error('HTTP 401')Auth errors are not transient — the client must not retry them.
- Do not modify
src/ApiClient.ts. - All assertions go in
tests/ApiClient.test.ts. - Use
vi.fn()for fetch mocking — no real HTTP calls.
The retry strategy here is a flat counter. Real systems use exponential backoff — each retry waits 2× longer than the last. How would you test that without making your test suite take seconds to run?
Keep moving through related testing problems and build a stronger search-friendly practice loop around this topic.
View track →