We often need to write end-to-end (e2e) tests to verify processes requesting secured resources, which demand appropriate credentials. For example, a login form expects a valid username and password, an end-point enforces a correct API key, and an HTTP request needs a verifiable token. We have a variety of approaches to protect these secrets in the e2e testing, which is a critical part of the continuous integration (CI) process. One of the common approaches is to save the secrets as environment variables, so that the test scripts can retrieve them.

Cypress provides several ways to work with environment variables. In its documentation about environment variables (link), Cypress has listed their usages, and compared the pros/cons for each option. In this article, we will focus on the option of passing in environment variables using the Cypress command line interface (CLI), and we will discuss scenarios of using the Cypress npm package and Cypress docker images.

For demo purposes, we will write e2e tests against a login page (link). The test username and password will be saved as environment variables, and passed to tests through the Cypress CLI. The e2e tests and the related commands can be found in my GitHub repository.

The e2e test

The following screenshot shows the login page under test. It is a generic testing page with a login form. In order to log into the website, we need to provide a string for username and a string for password.

None

In this contrived example, we already know the correct username and password because they are displayed in the login page. Let's make sure that we won't expose credentials to the public in real-world applications.

After a successful login, we should be able to see the following screen.

None

To keep the e2e test simple, we will only verify the happy path. The successful login results should include a URL containing a string "secure", an alert element having a CSS class of "success", and a heading h2 with text of "Secure Area".

Our goal is to avoid hard-coding the username and password in the test file, so that the secrets won't be committed to our code repository. The following code snippet shows an example e2e test for the login process.

In lines 5 and 6, Cypress accesses the environment variables to get values for the username and password. Then in lines 7 and 8, Cypress enters the username and password to submit the login form. Lines 10 to 13 are the assertions for successful results.

Environment variables in Cypress npm scripts

In this test, Cypress expects two environment variables: username and password, as indicated in lines 5 and 6.

We can certainly pass in these two environment variables using the following command. Note: in the Cypress CLI tool, environment variables are delimited by commas.

Cypress is able to parse the command and get defined values, and the e2e test passes.

However, we don't want to check in this command in clear text. For most CI platforms, we need to provide a configuration file to describe testing scripts, and we usually commit the configuration file in our version control system. Therefore, for the sake of securities in the CI pipeline, we need to have another layer of environment variables.

Most CI platforms support credentials plugins or something similar. In this case, we can store key-value pairs inside a CI platform, and the CI agent will replace the correct value when a key is found in the CI configuration file. Assume we have set up two parameters TESTUSERNAME and TESTPASSWORD in a CI platform, then we can use the following command in the CI configuration file.

Caution: if you don't like the syntax of $(npm bin) and prefer to use your own npm script, then please consider adding a -s flag to suppress the echo of interpreted script. For example, I have defined a script "cypress:run": "cypress run" in the package.json file, then the CI test command should be like the following.

To help you understand the effects of the -s flag, let's take a look at the following screenshot. Without the -s flag, npm will echo the interpreted command to the console, thus the CI logs will have the secrets in clear text, which maybe not appropriate.

None

Speaking of the CI logs, we might also be cautious about videos and screenshots generated by Cypress e2e tests, because we can see the password in the Cypress time travel recording. The following screenshot shows a snapshot of a generated video.

None

Knowing this, you might want to set video to be false in the cypress.json file, and suppress snapshots using Cypress API. Or, at least, we should keep the artifacts in a secure environment.

Environment variables in the test runner's profile

If our CI platform doesn't have the feature to store key-value pairs, then we can set the secrets to be part of the test runner's user environment variables, like the following.

The environment variables can be stored in the test runner's profile, such as in the ~/.bash_profile file. Then we can use them in a similar way mentioned in the section above.

However, it might be easier to just use Cypress-specific environment variables, which have the prefix CYPRESS_. For example, we can set the following environment variables in the ~/.bash_profile.

In this way, we don't need to pass the environment variables in the Cypress command. Instead, we can simply run the command npm run cypress:run, and the test will pass.

Environment variables for the docker image: cypress/included

Cypress provides a docker image cypress/included to allow us to run tests with only a single command. For example, to run the test in this article, we can go to the e2e folder, and execute the following command.

This command tells docker to run the image cypress/included:4.5.0 interactively, and mount the test folder as a volume /e2e in a docker container. The docker container will take the environment variables and run the Cypress tests.

Note that I put a slash / symbol before each volume path, because I run this command in Windows, and the slash symbol helps docker to correctly get volume paths in Windows.

In the scenarios of running docker containers, a CYPRESS_ environment variable won't work, because it only exists in the host.

Environment variables for a custom docker image

We may want to build a docker image and let the CI pipeline run tests in a container. In this case, we can write the following Dockerfile based on the Cypress image cypress/base.

We set the image's entry point as "cypress:run" command, so that when a container runs the image, the e2e tests will execute automatically. We can build the image and run it in a container using the following commands.

Very good. We see the test passes again, but in a docker container.

The underlying principle is to avoid checking in the credentials to the code repository, and in this article, we use environment variables to pass in secrets. I think I have covered all the scenarios that I am aware of, and I hope this article can help you decide how to use environment variables in your Cypress tests.

The complete project for the e2e tests can be found in my GitHub repository. Thanks for reading.