'mocking server for SSR react app e2e tests with cypress.io

I'm building a single page web application (SPA) with server side rendering (SSR).

We have a node backend API which is called both from the node server during SSR and from the browser after initial rendering.

I want to write e2e tests that configures API responses (like with nock) and work both with browser calls and SSR server calls. some pseudo-code :

it('loads some page (SSR mode)', () => {
  mockAPI.response('/some-path', {text: "some text"}); // here i configure the mock server response
  browser.load('/some-other-page'); // hit server for SSR response
  expect(myPage).toContain('some text');
})   

it('loads some other page (SPA mode)', () => {
  mockAPI.response('/some-path', {text: "some other text"}); // here i configure another response for the same call
  browser.click('#some-link'); // loads another page client side only : no SSR here
  expect(myPage).toContain('some other text');
})   

Currently Cypress allows me to mock fetch on the browser but not on the server.

Is there anything to achieve that ? Preferably with node libs.



Solution 1:[1]

MockTTP can do that. Excerpt from the doc :

const superagent = require("superagent");
const mockServer = require("mockttp").getLocal();

describe("Mockttp", () => {
    // Start your server
    beforeEach(() => mockServer.start(8080));
    afterEach(() => mockServer.stop());

    it("lets you mock requests, and assert on the results", () =>
        // Mock your endpoints
        mockServer.get("/mocked-path").thenReply(200, "A mocked response")
        .then(() => {
            // Make a request
            return superagent.get("http://localhost:8080/mocked-path");
        }).then(() => {
            // Assert on the results
            expect(response.text).to.equal("A mocked response");
        });
    );
});

Solution 2:[2]

We used a particularly ugly solution, that breaks the speed of cypress, but we needed that in order to mock/fake socket calls.

You can make a real simple express server that starts before running your tests. This 'real fake server' will be able to respond what you need. Here are the specs of our:

  • POST on / with method, path and {data} in body params in order to setup a route
  • GET/POST/PUT/DELETE on /path responds {data}
  • DELETE on / clear all the routes

Let's consider your 'real fake server' run on 0.0.0.0:3000; you'll do:

beforeEach() {
   cy.request('DELETE', 'http://0.0.0.0:3000/');
}

it('loads some page (SSR mode)', () => {
  cy.request('POST', 'http://0.0.0.0:3000/', {
    method: 'GET',
    path: '/some-path', 
    data: {text: "some other text"}
  }) // here i tell my 'real fake server' to 
     // respond {text: "some other text"} when it receives GET request on /some-path
  browser.load('/some-other-page'); // hit server for SSR response
  expect(myPage).toContain('some text');
})   

it('loads some other page (SPA mode)', () => {
  cy.request('POST', 'http://0.0.0.0:3000/', {
    method: 'GET',
    path: '/some-path', 
    data: {text: "some other text"}
  }); // here i configure another response for the same call
  browser.click('#some-link'); // loads another page client side only : no SSR here
  expect(myPage).toContain('some other text');
}) 

Important : the resquests need to be in localhost. You won't be able to stub something external. (Hence, make an env var in order to request localhost:xxxx instead of google.com when you test your app)

You won't be able to control the 'real fake server' otherwise than this cy.request because your tests scripts run in the browser (correct me if I'm wrong) and the browser can't run an express server.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 sylvain
Solution 2