How to configure Cypress for multiple testing environments

Cypress is a great tool for front-end testing. It provides features such as mocking HTTP responses, stubbing objects and methods, and simulating user interaction.

It can be used as a fully-fledged front-end testing framework for unit, integration and e2e tests. This post assumes you are already somewhat familiar with Cypress and its methodologies, and it serves as a guide for advanced configuration which can help you structure and organize your tests. So, let’s get straight to the point.

When first installing and running Cypress with npm or yarn, it will generate some default tests and configuration for you.

At the time of writing, these files and directories are:

// configuration
./cypress.json 
// directories with respective files / tests
./cypress/fixtures/ 
./cypress/integration/examples/
./cypress/plugins/
./cypress/support/

The default command for running Cypress in interactive mode is ./node_modules/.bin/cypress open and it will, by default, use cypress.json as a configuration file.

An example of a configuration file:

{
    "baseUrl": "http://localhost:3000",
    "integrationFolder": "cypress/integration",
}

Cypress will also, by default, run all tests from the integration directory, supporting nested directories. We could add all our unit, integration and e2e tests to the integration directory, but we want to configure some things differently – depending on our environment. For example, we could avoid some API calls from the app by completely blocking the host from the configuration file.

Let’s say we want to mock the response for the foo resource from our API. In our Cypress test, we’ll do something like this:

describe("foo", () => {
    it("should fetch and render foo", () => {
      cy.server();
      // load fixture and mock response
      cy.fixture("foo").then((response) => {
        cy.route({
          method: "GET",
          // you can also setup base url in cypress config
          // it can be used as Cypress.env('BASE_API_URL')
          url: "https://api.my-app.com/foo/**",
          response: response,
        });
     });
     // …rest of the test
}));

By specifying the same host of our backend API in blacklistHosts, Cypress will allow creating mock responses for the blocked domain, but it will intercept and disallow any other response to that same domain. This could be a handy setup for integration testing, where you want to test a feature in a very specific scenario, mocking all the requests and data the application needs. This is a powerful configuration, but for e2e tests, we don’t want to block the real API because we need to test front-end with the real back-end services. Therefore, we need to split our configs.

We’ll first create a cypress-integration.json file and place it inside the auto-generated cypress directory. And then we specify another path for the location of the tests – for our use case, that is cypress/tests/integration. Notice the change from integration to tests naming. The naming doesn’t need to be replicated, so feel free to use directory names which suit you and your team.

Example cypress-integration.json file:

{
    "baseUrl": "http://localhost:3000",
    "integrationFolder": "cypress/tests/integration",
    "blacklistHosts": ["api.my-app.com"]
}

Now, to run only those tests suites for integration tests, we need to add commands in our package.json. “cypress:open:integration” is for the “interactive” mode, and cypress:run:integration is for running in “CI” mode.

{
 "scripts": {
   "cypress:open:integration": "cypress open --config-file cypress/cypress-integration.json",
   "cypress:run:integration": "cypress run --config-file cypress/cypress-integration.json"
 }
}

Now, you can repeat this process for other environments, such as e2e, storybook or unit tests, and provide different configurations for them. That’s it, thank you for reading!