Mock process.env In Your Cypress React Tests

How to mock the .env values in your Cypress end-to-end tests for React applications.

Let's take a React application that uses REACT_APP_... values from the .env file in its code base. We can take the app from the blog post "Using .env file in React js" as an example. It's .env file has the following variable values:

.env
1
2
3
4
REACT_APP_TITLE=How To React
REACT_APP_DESCRIPTION=UsingĀ .env file in ReactĀ js
REACT_APP_DEV_MODE=This is a development mode
REACT_APP_PRO_MODE=This is a production mode

The App.js component uses these values in its output

src/App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react'
import './App.css'

function App() {
return (
<div className="App">
<h1>{process.env.REACT_APP_TITLE}</h1>
<h3>{process.env.REACT_APP_DESCRIPTION}</h3>

{process.env.NODE_ENV === 'development'
? process.env.REACT_APP_DEV_MODE
: process.env.REACT_APP_PRO_MODE}
</div>
)
}

export default App

If we start the application locally and visit localhost:3000 we see these values on the page

React application page

Nice.

Text substitution

If we inspect the JavaScript bundle created by the react-scripts and served in the browser, we won't find process.env.REACT_APP_TITLE

Variable name is not found

Instead, the bundler substitutes the string values right into the source code

Env text used inside the bundle

If we print the entire process.env object, it gets substituted into the source bundle

1
2
3
4
function App() {
console.log(process.env)
return (
...

The entire process.env object is replaced in the source code

This means, we cannot even overwrite a property of the process.env object!

1
2
3
4
5
function App() {
console.log(process.env)
process.env.REACT_APP_TITLE = 'Hello!'
return (
...

Cannot override the process.env property

Hmm, can we dynamically replace those values while running Cypress tests?

Make it testable

Just like we sometimes add attributes to make our HTML elements easy to find from our tests, we should adjust the source code in the App.js a little to make the process.env possible to overwrite. Here is my trick: check the window.process.env object with fallback to the process.env text. React bundler does not overwrite window.process.env object.

src/App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from 'react'
import './App.css'

function App() {
const env = { ...process.env, ...window.process?.env }
return (
<div className="App">
<h1>{env.REACT_APP_TITLE}</h1>
<h3>{env.REACT_APP_DESCRIPTION}</h3>

{env.NODE_ENV === 'development'
? env.REACT_APP_DEV_MODE
: env.REACT_APP_PRO_MODE}
</div>
)
}

export default App

If window.process.env object exists, it overwrites anything in the process.env inserted by the bundler. Thus we can overwrite only the properties we want by setting them on the application's window object. Here is a typical test example:

cypress/e2e/spec.cy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
it('mocks process.env', () => {
// no mocking
cy.visit('/')
cy.contains('h1', 'How To React').wait(1000)
// mocking
cy.visit('/', {
onBeforeLoad(win) {
win.process = {
env: {
REACT_APP_TITLE: 'How To Test',
},
}
},
})
cy.contains('h1', 'How To Test')
})

Mocking a process.env property

Mocking via network intercept

We can mock the text in the JavaScript bundle in other ways. We can intercept the bundle.js resource loaded by the page. We can find and replace any text strings we want there using cy.intercept command.

1
2
3
4
5
6
7
8
9
10
11
12
13
it('replaces strings in the JS bundle', () => {
cy.intercept('GET', 'bundle.js', (req) => {
// avoid the browser sending 304 cached response
delete req.headers['if-none-match']
req.continue((res) => {
res.body = res.body.replaceAll('"How To React"', '"How To Test"')
})
}).as('bundle')
cy.visit('/')
// confirm the server sent the response
cy.wait('@bundle').its('response.statusCode').should('equal', 200)
cy.contains('h1', 'How To Test')
})

Overwriting the string in the JS bundle

If React bundler can overwrite process.env.REACT_APP_TITLE with "How To React", then we can certainly overwrite it with "How To Test".

šŸŽ“ If you want to master Cypress network testing in depth, consider studying my course Cypress Network Testing Exercises.

Component testing

What about Cypress Component testing? We cannot use cy.intercept to intercept the bundle loaded by the page during cy.visit('/') unfortunately. The bundle is already served by the time the test runs. We can still use the window.process.env trick:

src/App.cy.js
1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react'
import App from './App'

it('loads', () => {
window.process = {
env: {
REACT_APP_TITLE: 'How To Component Test',
},
}
cy.mount(<App />)
cy.contains('h1', window.process.env.REACT_APP_TITLE)
})

Testing mocking the process.env property from a Cypress component test

See also