API Testing with Taiko: Intercepting Requests and Validating Responses

API Testing with Taiko: Intercepting Requests and Validating Responses

Taiko runs on Chrome DevTools Protocol, which gives it direct access to the browser's network layer. You can intercept requests, mock API responses, stub network calls, and validate that your front-end is making the right API calls. This makes Taiko useful beyond pure UI automation — it bridges browser and API testing.

Intercepting Network Requests

Taiko's intercept() function captures or modifies requests matching a URL pattern:

const { openBrowser, goto, intercept, closeBrowser } = require('taiko');

// Log all API calls
await intercept('https://api.example.com/users', (request) => {
    console.log('API call:', request.url, request.method);
    request.continue();
});

await goto('https://example.com/dashboard');

Mocking API Responses

Return a mock response instead of making the real network call:

const { openBrowser, goto, intercept, click, text, closeBrowser } = require('taiko');

async function testWithMockedAPI() {
    await openBrowser({ headless: true });

    // Mock the users endpoint
    await intercept('https://api.example.com/users', {
        body: JSON.stringify([
            { id: 1, name: 'Alice Johnson', role: 'admin' },
            { id: 2, name: 'Bob Smith', role: 'viewer' }
        ]),
        headers: { 'Content-Type': 'application/json' },
        status: 200
    });

    await goto('https://example.com/users');

    // Assert the UI renders mocked data correctly
    assert.ok(await text('Alice Johnson').exists());
    assert.ok(await text('Bob Smith').exists());

    await closeBrowser();
}

Mocking Error States

Test how your UI handles API failures:

// Mock a 500 error
await intercept('https://api.example.com/orders', {
    body: JSON.stringify({ error: 'Internal server error' }),
    status: 500
});

await goto('https://example.com/orders');
assert.ok(await text('Something went wrong').exists());

// Mock a 404
await intercept('https://api.example.com/users/999', {
    body: JSON.stringify({ error: 'User not found' }),
    status: 404
});

Mocking Slow Responses

Simulate network latency to test loading states:

await intercept('https://api.example.com/reports', async (request) => {
    await new Promise(resolve => setTimeout(resolve, 3000)); // 3 second delay
    request.continue({
        body: JSON.stringify({ data: [] }),
        status: 200
    });
});

await goto('https://example.com/reports');
assert.ok(await text('Loading...').exists());         // loading state shown
await waitFor(() => text('No reports found').exists()); // final state appears

Capturing API Calls for Validation

Verify that user actions trigger the correct API calls:

const capturedRequests = [];

await intercept('https://api.example.com/**', (request) => {
    capturedRequests.push({
        url: request.url,
        method: request.method,
        body: request.postData
    });
    request.continue();
});

// Perform UI action
await goto('https://example.com/checkout');
await click(button('Place Order'));
await waitFor(() => text('Order confirmed').exists());

// Validate API call was made correctly
const orderRequest = capturedRequests.find(r => r.url.includes('/orders'));
assert.ok(orderRequest, 'POST /orders was called');
assert.strictEqual(orderRequest.method, 'POST');

const body = JSON.parse(orderRequest.body);
assert.ok(body.items.length > 0, 'Order has items');

Combining UI and API Assertions

A complete test that validates both the UI and the API call:

const { openBrowser, goto, write, click, button, inputField, 
        toRightOf, waitFor, text, intercept, closeBrowser } = require('taiko');
const assert = require('assert');

describe('User registration', () => {
    let apiRequests = [];

    before(async () => {
        await openBrowser({ headless: true });

        // Capture all API calls
        await intercept('https://api.example.com/**', (req) => {
            apiRequests.push({ url: req.url, method: req.method, body: req.postData });
            req.continue();
        });
    });

    after(async () => {
        await closeBrowser();
    });

    beforeEach(() => {
        apiRequests = [];
    });

    it('submits correct registration data', async () => {
        await goto('https://example.com/register');

        await write('Alice', into(inputField(toRightOf('First Name'))));
        await write('Johnson', into(inputField(toRightOf('Last Name'))));
        await write('alice@example.com', into(inputField(toRightOf('Email'))));
        await write('securepass123', into(inputField(toRightOf('Password'))));

        await click(button('Create Account'));

        // UI assertion
        await waitFor(() => text('Welcome, Alice').exists());
        assert.ok(await text('Welcome, Alice').exists(), 'Welcome message shown');

        // API assertion
        const registerCall = apiRequests.find(r => r.url.includes('/users') && r.method === 'POST');
        assert.ok(registerCall, 'POST /users API call was made');

        const payload = JSON.parse(registerCall.body);
        assert.strictEqual(payload.email, 'alice@example.com');
        assert.strictEqual(payload.firstName, 'Alice');
        // Password should not be logged — just verify it was sent
        assert.ok(payload.password, 'Password was included in request');
    });

    it('handles duplicate email error', async () => {
        // Mock the API to return a conflict error
        await intercept('https://api.example.com/users', {
            body: JSON.stringify({ error: 'Email already in use' }),
            status: 409
        });

        await goto('https://example.com/register');
        await write('existing@example.com', into(inputField(toRightOf('Email'))));
        await click(button('Create Account'));

        // UI should display the error
        await waitFor(() => text('Email already in use').exists());
        assert.ok(await text('Email already in use').exists());
    });
});

Intercepting Auth Tokens

Useful for testing authenticated flows without going through the login UI:

// Inject an auth token into requests
await intercept('https://api.example.com/**', (request) => {
    request.continue({
        headers: {
            ...request.headers(),
            'Authorization': `Bearer ${process.env.TEST_AUTH_TOKEN}`
        }
    });
});

// Skip login, go directly to protected page
await goto('https://example.com/admin/settings');

Stubbing Specific Endpoints

When testing a page that makes multiple API calls, stub only the slow or flaky ones:

// Real API for most calls
// Mock only the analytics endpoint (slow, unreliable in staging)
await intercept('https://analytics.example.com/**', {
    body: JSON.stringify({ events: [] }),
    status: 200
});

await goto('https://example.com/dashboard');
// Dashboard loads fast because analytics is mocked

Testing File Downloads

Intercept download requests:

let downloadUrl = null;

await intercept('https://api.example.com/exports/**', (request) => {
    downloadUrl = request.url;
    request.continue();
});

await click(button('Export CSV'));
await waitFor(() => downloadUrl !== null);
assert.ok(downloadUrl.includes('format=csv'), 'CSV format requested');

Pattern Matching in Intercept

intercept() supports glob-style patterns:

// All requests to the API domain
await intercept('https://api.example.com/**', handler);

// Specific resource type
await intercept('https://api.example.com/users/**', handler);

// Query string match
await intercept('https://api.example.com/search?*', handler);

Limitations

Taiko's network interception is browser-level — it only captures requests made by the browser. It does not:

  • Intercept server-side HTTP calls
  • Work with WebSocket connections (no interception support)
  • Capture requests made outside Chrome (e.g., from Node.js backend during SSR)

For server-side API testing, use a dedicated HTTP client like axios or node-fetch alongside Taiko.

Summary

Taiko's network interception closes the gap between browser and API testing. You can validate that UI actions trigger correct API calls, test error states without a flaky backend, stub slow endpoints, and combine UI and API assertions in the same test file. For teams doing full-stack feature testing, this eliminates the need to maintain separate browser test and API test suites for the same features.

Read more

Start now free