In most real life projects you’ll want to run your E2E tests against multiple environments. Let’s imagine that you have a local environment, which in this example means you run the application under test on your local machine. Naturally the URL to access this service will be localhost
or 127.0.0.1
. Now imagine that you also have a dev, test, tni and staging environment. These environments won’t have the same URL assigned to them. Simple differences like this could be solved by using environment variables within Cypress.
However, when the story becomes more complex, environment variables might not serve your needs that well. What if the service under test has a login screen and you need to supply a username and password which differ per environment? Where are you going to store this configuration? Not in environment variables! You’ll quickly end up with dozens of variables, which will become an unmanageable nightmare. But what is the solution?
Personally, I’ve been a fan of environment settings files. These files hold all the needed information which differs between environments. This implies that we’d have a seperate file for each environment where the service is running. Within each file we will describe the unique information needed for a service within the corresponding environment.
Cypress already brings part of the solution to the table. As some of you may already know, there is a cypress.json
file which makes it possible to override Cypress’ default settings and introduce your own. The issue here is that Cypress only support one of these files by default. Luckily for us Cypress does support a method to load this config file from a custom path. We will exploit this method, in combination with setting a single environment variable, to introduce our settings file per environment.
Reading in the single environment variable and using it to control which environment files we want to load, can be done by simply adding some code to the index.js file within the Cypress folder. This file is generally used to load plugins and will be executed automatically whenever the project is opened by Cypress.
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to the project's config changing)
// promisified fs module
const path = require('path');
const fs = require('fs-extra');
function getConfigurationByFile(file) {
const pathToConfigFile = path.resolve(
'cypress/config',
`cypress.${file}.json`
);
return fs.readJson(pathToConfigFile);
}
module.exports = (on, config) => {
// accept a configFile value or use local by default
const file = config.env.configFile || 'local';
return getConfigurationByFile(file);
};
The block of code above will read in the environment variable called configFile
, if configFile
was not set then it will use local
as a default value. For example, if configFile=dev
then the block of code above will go looking for the config file in <project folder>/cypress/config/cypress.dev.json
. Feel free to alter the block of code to look for the config file in whichever path you desire. Don’t forget to add fs-extra dependency to your package.json file. Starting Cypress for the dev environment would look like the following command cypress open --env configFile=dev
.
If you, for example, store the URL for the current environment in a variable called baseUrl, your config file would look like this:
{
"baseUrl": "https://www.google.com"
}
Then you can access the baseUrl variable using following line of code: Cypress.config().baseUrl
.
You can also use this method to store variables in the environment of Cypress. To achieve this, one can simply add an env block within the config file, as shown in the example below:
{
"baseUrl": "https://www.google.com",
"env": {
"test": "hello world"
}
}
This variable can then be exposed by using Cypress.env().test
which will return hello world
.
Using these settings files to configure your different environments has some pleasant upsides. One of those is the inclusion of the files in the repository which means it is tracked and stored exactly like the rest of your code. Do you have a unique environment file that you don’t want to check and share with your colleagues, to reduce clutter in the repository? No problem, just add it to your gitignore file or equivalent.
Putting settings inside of a file instead of dozens of environment variables is also less prone to mistakes and omissions of data, saving you time and headaches.
This approach has two big downsides which can partly be mitigated. First, these files will only be processed upon starting Cypress. This also counts for changing environment since you tell Cypress which environment to use on startup. So you will have to restart Cypress after you edit one of these files for the changes to take effect.
The second downside is that accessing the data from the environment files can clutter the spec file a little and make it less readable. My recommendation here is to make use of your page objects where you can hide this logic, keeping your spec files clean and fluent. Luckily accessing the data doesn’t take too much code and is usually just a one liner.
Enjoy and happy testing