Cypress : Managing test environment URLs and app secrets

Sanjeev Kumar
7 min readOct 6, 2022

--

It can be quite challenging to maintain test automation framework which handles multiple URLs for an application and uses different kinds of tokens/secrets in various environments.

In automation frameworks, it is advised not to hardcode urls, secrets, and other environment-specific variables, as this can result in flaky tests, security gaps and high maintenance costs.

The purpose of this blog is to illustrate how Cypress is able to manage multiple URLs and app secrets across different testing environments.

About Target Application for Demo

So to demonstrate how we can manage different urls ,I am going to use “https://api.realworld.io/” url as an application under test.

This application has multiple exposed API endpoints like login, Get Articles, Create Articles, etc and for each API, the URL path or endpoint is different except for the base URL.

But in Development environment , base url also changes based on the environment type used. For example , if it is a dev environment’s url then the target url could be like this “https://api.dev.realworld.io/” and if it is QA environment url then it could be like this “https://api.qa.realworld.io/”.

Let’s assume that application target url for RealWorld app will get changed based on the Environment type used for the demo.

How to handle multiple URLs in Cypress

Note — I am using Cypress 10.0+ version for demo purpose. Approach for Cypress version less than 10.0 is also similar .

As it can be seen in the following cypress.config.ts file that a base URL has been added for a development environment. For each type of environment, you must create a separate configuration file for your baseURL. This config file name needs to be included in your Cypress run command.

Isn’t it overwhelming to have multiple files for one (baseUrl) value change?

cypress.config.ts file

Also, after adding the base URL to the config file, we hard code the API endpoints like this:

e2e test

But, this is not a good practice in terms of writing maintainable tests.

Let’s look at how the environment URLs can be handled dynamically in the test instead of setting the base URL in the config file.

Cypress provides the env object for setting and accessing environment variables dynamically as shown in the screenshot below:

Cypress config objects

However, the env object is empty currently, so let’s create a custom configuration file that will return the list environment URLs in env object.

custom-env-config.ts file

Under the config folder, I have created the file “custom-env-config.ts”. Within this config file, we have a variable called getEnvironmentUrls, which returns an Environment object with application-specific URLs inside.

As you can see, an arrow function has been assigned to the getEnvironmentUrls variable, and this arrow function accepts an envType variable that will allow us to dynamically set the environment URLs based on Type.

The next step is to pass the envType to this arrow function.

Let’s begin by going to the cypress.config.ts file and replacing the Cypress env object with our own.

cypress.config.ts

This screenshot shows how I am capturing the environment type using command line arguments(process.env) and passing it to the getEnvironmentUrls arrow function, which will return the custom env object.

I will run the Cypress test with the following command without any arguments to see how it works.

npx cypress run
Console output

Since, we didn’t provide any command line arguments in the test script run, the environment is set as undefined, but at least we can set urls dynamically.

Now let’s pass the environment type as a command line argument in the test script.

ENVIRONMENT_TYPE=qa npx cypress run

So, we have got the URLs as per our environment type, but again we have to perform one more step to assign this value to the Cypress env object.

Now, we are going to use Object.assign() to copy from source object to target object and it will return the updated object.

const envBasedRunCypressConfig = getEnvironmnetUrls(ENVIRONMENT_TYPE);// Copy from envBasedRunCypressConfig.env to update the cypress config.envObject.assign(config.env, envBasedRunCypressConfig.env);console.log("config:", config);// return this updated config 
return config;
Final updated cypress.config.ts file
Result

Now, we have got the desired urls in Cypress env object , so let see how we can use this in test scripts.

I am going to user Cypress.env() to get the value of key inside env object.

Cypress Test using Cypress.env()

As you can see above, instead of using hard-coded API endpoints, we are fetching the URL dynamically based on environment type.

Let us see the test result:

Test Result

Since, this url ‘https://api.qa.realworld.io/api/users/login’ is created by us and it is not available hence it causing the test failure.

This is how you can add as many application related urls inside custom env object dynamically based on env type.

Note — The only downside I see here is that, if baseUrl is empty in the config file, Cypress will refresh the browser at the start (first time it will load the baseUrl and if baseUrl is empty then it will load the url which you will provide in cy.visit(‘url’) function).

How to handle App Secrets/ Tokens in Cypress

Security good practice recommends not hardcoding app secrets or tokens inside the repository. Therefore, it’s essential to manage secrets externally.

Jenkins server and other CI tools allow you to manage app secrets in CI/CD by storing secrets and other sensitive information in the form of credentials, which can then be passed to run scripts as arguments.

I will execute Cypress tests in Jenkins CI server and pass the secrets to the scripts.

So, you need to add your app secrets in Jenkins Global Credential and if you do not have the access to the same then do ask the infra admin to add the required tokens/secrets in Global Credential.

App Secrets in Jenkins Global Credentials

Once it is done then you need to follow the same steps which I have explained above for configuring multiple urls.

A secret key needs to be added to the env object first and its value will be derived from the command line argument.

Jenkins will provide the secret via a command line argument if we run the tests in pipeline, but if we run them locally, you can manually provide the secret to test scripts.

//We need to add APP_SECRET to store the value 
const { ENVIRONMENT_TYPE, APP_SECRET } = process.env;
// Inside defineConfig , pass the APP_SECRET to getEnvironmnetUrls function to set the value in env objectconst envBasedRunCypressConfig = getEnvironmnetUrls(ENVIRONMENT_TYPE,APP_SECRET);

Now , we also need to add key for app secret inside custom-env-config.ts file as shown in below screenshot:

custom-env-config.ts

Let’s take a look at how secret can be passed to a test script in Jenkins.

In the first step, we need to get the value from Jenkins global credentials using the ID value. Once we have this value, we need to pass it to the script as a command line arguments.

See the below code:

This is how you can fetch the value and pass it to the test scripts , but if you are not using Jenkins pipeline as a code then you can provide this value directly by UI by selecting the option ‘This project is parameterised” .

Conclusion

This is how you can manage multiple urls and app secrets in Cypress test automation using env objects.

It’s an approach I’m using in my current project. From my experience, it is quite effective and efficient for writing maintainable tests.

Thanks for reading!!

See you in another posts.

--

--

Sanjeev Kumar

New to blogging, but not to the world of testing! With 7.7 years of automation testing experience, I'm here to share my insights, tips, and tricks with you all.