How to use a saved network log for automatic mocking

When writing end-to-end tests it is often useful to mock all calls to the back-end and to test only the front-end UI end-to-end.

Why not use the real back-end?
  • Unreliable endpoints: The endpoints might not be ready yet, be in flux, or be unavailable at the time that the test runs

  • Slow tests: The round trips add time to the tests

  • Test data availability: The data needs to be ready for the tests and to be in a sandbox just for the tests, so that we can have reliable and accurate assertions about the rendered information

Why not use a mock back-end server?
  • Syncing: The mock back-end will need to be kept in sync with the actual back-end

  • False positives: Tests might pass against the mock back-end but not against the real back-end

  • Maintenance: There is a some maintenance required for each test to provide the right data

Are the tests reliable if we use mock data?
  • It can be toggled! We can toggle the mock data in order to use the real back-end with the exact same suite of tests. But using mock data can offer us a much quicker development experience.

How to use a HAR file for mocks with Cypress/Selenium or similar

Once we have a HAR file, we should do some processing to filter and map the entries. There is typing available if you’re using TypeScript.

The most basic structure we can extract is below:

interface HarMock {
    request: {
        method: string;
        url: string;
        postData: any;
    };
    response: {
        status: number;
        statusText: string;
        content: {
            mimeType: string;
            text: any;
        };
    };
}

We can make this file more consumable by adjusting a few things:

  • Filter by only the back-end endpoints we are interested in (we can throw away a lot of analytics and other calls unless these are needed for the tests)

  • Filter only the fields above (E.g we can remove timing information for how long a request took)

  • Ignore any known heavy responses that are duplicated across many tests (mock these individually)

Once we have this modified HAR file, we can store this with our other mocks and reference it within our tests. The reduced file size can be very manageable.

Cypress currently has a limitation in that it can only mock by method and URL. As a consequence if you make multiple requests to the same endpoint we can’t specify a sequence of responses. We need to wait for one to complete before re-mocking that endpoint. A workaround is to load this HAR file and to expose it to the tests so that the tests can do this manual work of advancing the sequence.

With this information we should be able to mock our endpoints all at once using the HAR file. We can also set the clock and locale to be the same as it was used when the HAR file was created, so that we aren’t at the mercy of time- or timezone-related issues.

When creating mock routes by hand, we can often create mock data that is in different timezones or times, and our assertions have to take this into account. For example, mocking the data with an epoch timestamp and using an assertion against the formatted value could display something different for each different timezone or locale that the tests is run in, which leads to brittle tests.

// Mock data (Sept 13, 2020 at 00:00:00 GMT)
{ dateCreated: 1599955200000 }

// Assertion (will fail in locales West of GMT, for example)
cy.get('[data-testid="dateCreated"]').should('contain', '13/09/2020');

For this reason using a HAR mock and setting the clock and timezone in the tests to the ones used when creating the HAR file leads to consistent results.