New Jun 7, 2026

Your Test Suite Shouldn't Depend on APIs You Don't Control

The Giants All from DEV Community View Your Test Suite Shouldn't Depend on APIs You Don't Control on dev.to

You know this feeling. A test passes locally, fails in CI, you rerun it, it passes again. Somewhere a third-party API was slow, or returned something unexpected, or was just briefly down. You move on, but it happens again next week.

And that's before you even get to the scenarios you can't test at all: rate limits, timeouts, malformed responses. You can't make a live API produce those on demand. So those code paths go untested.

Mocking solves both problems. Mokapi gives you a spec-validated mock server driven by your OpenAPI or AsyncAPI specs. Every response conforms to the contract. Every request gets validated. Your tests run against something you fully control.

The CI Setup

Mokapi runs in Docker during your test job. Your app points at it instead of the real API. That's the whole switch.

- name: Start Mokapi
  run: |
    docker run -d --name mokapi \
      -p 80:80 \
      -v ${{ github.workspace }}/mocks:/mocks \
      mokapi/mokapi:latest /mocks
    sleep 5

- name: Run Tests run: npm test

- name: Stop Mokapi run: docker stop mokapi

Your mock specs live in /mocks in the repo, versioned alongside the code. When the API contract changes, the spec changes, the mock changes, and the tests run against the updated contract automatically.

Simulating the Scenarios That Matter

This is where it gets useful beyond just removing the external dependency. With Mokapi's JavaScript API you can control behavior at runtime without restarting anything.

import { on, sleep } from 'mokapi'

export default () => { let delay = undefined

on('http', (request, response) => { if (request.key === '/simulations/delay') { switch (request.method) { case 'PUT': delay = request.query.duration break case 'DELETE': delay = undefined break } } }, { track: true })

on('http', (request, response) => { if (delay) { sleep(delay) } }, { track: () => delay !== undefined }) }

Your test sends a PUT to enable a delay, runs the scenario, sends a DELETE to reset. Same pattern works for forcing 500s, 429 rate limits, or any other condition you want to test against.

The track: true on the first handler is worth understanding. Mokapi's dashboard shows every HTTP request with the response and all JavaScript handlers that ran for it. The first handler only updates internal state without touching the response, so Mokapi wouldn't know it was relevant unless you tell it explicitly. track: true keeps it visible in the dashboard, which matters when you're debugging why a test behaved unexpectedly.

What You Actually Get

Consistent test results because nothing outside your control can break them. Coverage of error scenarios that a live API can't reproduce on demand. Faster pipeline runs with no network latency. And because Mokapi validates against the spec, contract violations get caught in CI before they reach production.

Full Walkthrough

The complete guide on mokapi.io covers the full GitHub Actions setup, the JavaScript simulation API, and how to structure your mock specs for a real project.

👉 Read the full guide here

Happy to answer questions in the comments.

Scroll to top