If you are a Node.js developer, installing Cypress as a dev dependency in your
package.json file or even via direct download seems simple enough. Yet for developers working in other languages like Python or Go, using npm can be an obstacle. We often hear this question from developers:
Why don’t you have a Docker image with Cypress pre-installed?
Having an image with both OS dependencies and the Cypress test runner ready to go seems to be very convenient, which is well described in this blog post “End-to-End Testing Web Apps: The Painless Way” by Michael Lynch.
We have listened and created a Docker image with Cypress installed. It is called
cypress/included and is tagged with the version of Cypress installed in the image. For now, we'll use the image for the current Cypress version as of this writing - the
cypress/included:3.2.0 image. We create new images for every Cypress version released. The image is built on top of the
cypress/base:12.1.0 image with Node 12.1.0 included. You can find our official Docker images in the cypress-docker-images repository and on Docker hub.
You can find the examples below in the repo demo-docker-cypress-included.
If you have Cypress end-to-end tests, you can run them using the complete image. For example, if your project structure looks like this:
cypress/ integration/ spec.js cypress.json
Then you can execute your Cypress tests using the following shell command:
$ docker run -it -v $PWD:/e2e -w /e2e cypress/included:3.2.0 ============================================================================== (Run Starting) ┌──────────────────────────────────────────────────────────────────────────┐ │ Cypress: 3.2.0 │ │ Browser: Electron 59 (headless) │ │ Specs: 1 found (spec.js) │ └──────────────────────────────────────────────────────────────────────────┘ ────────────────────────────────────────────────────────────────────────────── Running: spec.js... (1 of 1) Cypress TodoMVC test ✓ adds 2 todos (1351ms) ...
Explanation of the "docker run" command line arguments -it = interactive terminal -v $PWD:/e2e = map current folder to /e2e inside the container -w /e2e = set working directy to /e2e
Nothing to install, just write your spec files using your favorite editor and run them.
cypress/included:3.2.0 has the entrypoint set to
cypress run, so you don’t need to type it when running our Docker image. If you want a different command, you can change the entrypoint and then pass any additional arguments after the image name.
$ docker run -it -v $PWD:/e2e -w /e2e --entrypoint=cypress cypress/included:3.2.0 help Usage: cypress [options] [command] Options: -v, --version prints Cypress version -h, --help output usage information Commands: help Shows CLI help and exits version prints Cypress version run [options] Runs Cypress tests from the CLI without the GUI open [options] Opens Cypress in the interactive GUI. install [options] Installs the Cypress executable matching this package's version verify Verifies that Cypress is installed correctly and executable cache [options] Manages the Cypress binary cache
You can also pass environment variables into the container to control Cypress behavior. For example, the boolean config option
video controls if the video of the run is recorded. It is
true by default, but you can disable it via an environment variable.
$ CYPRESS_VIDEO=false $ docker run -it -v $PWD:/e2e -w /e2e -e CYPRESS_VIDEO cypress/included:3.2.0
If you want to record the test run on the Cypress Dashboard to review later, you need to pass the record key and the
--record CLI flag.
$ docker run -it -v $PWD:/e2e -w /e2e \ -e CYPRESS_RECORD_KEY cypress/included:3.2.0 --record
To show the information about the operating system and pre-installed browsers, you can execute cypress info command:
$ docker run -it -v $PWD:/e2e -w /e2e --entrypoint=cypress cypress/included:6.2.1 info Displaying Cypress info... Detected 2 browsers installed: 1. Chrome - Name: chrome - Channel: stable - Version: 87.0.4280.66 - Executable: google-chrome 2. Firefox - Name: firefox - Channel: stable - Version: 82.0 - Executable: firefox Note: to run these browsers, pass <name>:<channel> to the '--browser' field Examples: - cypress run --browser firefox - cypress run --browser chrome Learn More: https://on.cypress.io/launching-browsers Proxy Settings: none detected Environment Variables: CYPRESS_CACHE_FOLDER: /root/.cache/Cypress Application Data: /root/.config/cypress/cy/development Browser Profiles: /root/.config/cypress/cy/development/browsers Binary Caches: /root/.cache/Cypress Cypress Version: 6.2.1 System Platform: linux (Debian - 10.5) System Memory: 2.09 GB free 534 MB
We can see both Chrome and Firefox browsers pre-installed in the
cypress/included:6.2.1 image. Let's run our tests using Firefox browser for example:
$ docker run -it -v $PWD:/e2e -w /e2e cypress/included:6.2.1 --browser firefox ======================================================================== (Run Starting) ┌──────────────────────────────────────────────────────────────────────┐ │ Cypress: 6.2.1 │ │ Browser: Firefox 82 │ │ Specs: 1 found (spec.js) │ └──────────────────────────────────────────────────────────────────────┘ ...
Running the tests inside a Docker container is nice, but it is missing my favorite Cypress feature: its interactive Test Runner with the Command Log, time-traveling debugger and live view of what is going on during the test! Typically, you would execute
cypress open to open the Test Runner in interactive mode, but how do we see it if Cypress opens inside a Docker container?
If you want to see Cypress in interactive mode, you need to forward the XVFB messages from Cypress out of the Docker container into an X11 server running on the host machine. I have done this on my Mac; other operating systems might require different commands.
Then I grabbed the IP of the host machine and added it to the allowed X11 hosts.
$ IP=$(ipconfig getifaddr en0) $ /usr/X11/bin/xhost + $IP 10.0.0.124 being added to access control list
Now you can execute a
cypress open command passing
DISPLAY and the X11 socket file to the container:
DISPLAY=$IP:0 docker run -it \ -v $PWD:/e2e \ -v /tmp/.X11-unix:/tmp/.X11-unix \ -w /e2e \ -e DISPLAY \ --entrypoint cypress \ cypress/included:3.2.0 open --project .
Explanation of the "docker run" command line arguments: -it = interactive terminal -v $PWD:/e2e = map current folder to /e2e inside the container -v /tmp/.X11-unix:/tmp/.X11-unix = map X11 socket file to communicate -w /e2e = set working directy to /e2e -e DISPLAY = pass environment variable DISPLAY to the container --entrypoint cypress = run "cypress" command with arguments AFTER Docker image name in our case they are "--project ." to point globally installed Cypress at the current working directory /e2e inside the container
The Docker container starts and you can see the full interactive Cypress Test Runner open. You can watch the test run, interact with the Command Log, open DevTools, etc. Even spec file watching is working - if you edit and save the
cypress/integration/spec.js file, the Test Runner picks up the change and reruns the tests.
Debugging tip: if Cypress shows an error
Gtk-WARNING **: cannot open display:... make sure X11 server allows connections over the network from the Docker container. Run
xhost command in the terminal to see if it has the IP address you have added previous with
xhost + $IP.
Using docker-compose to spawn services and run end-to-end tests is very convenient. We have coded several examples that show how to run a web application and Cypress tests in two Docker containers:
- cypress-open-from-docker-compose which is a fork of mtlynch/hello-world-cypress
To support both
cypress run and
cypress open settings we recommend:
- placing the default settings for
docker-compose.ymlfile, for example, like this
# e2e/docker-compose.yml from repo # https://github.com/bahmutov/cypress-open-from-docker-compose version: '3.2' services: # this is the web application we are going to test sentimentalyzer: build: ../ environment: - PORT=8123 # Cypress container cypress: # the Docker image to use from https://github.com/cypress-io/cypress-docker-images image: "cypress/included:3.2.0" depends_on: - sentimentalyzer environment: # pass base url to test pointing at the web application - CYPRESS_baseUrl=http://sentimentalyzer:8123 # share the current folder as volume to avoid copying working_dir: /e2e volumes: - ./:/e2e
You can start the application, run the headless tests and close the services with command:
docker-compose up --exit-code-from cypress
- place the X11 configuration that enables
cypress openTest Runner to show on the host machine in a separate YAML file that extends the above file. The second file only has additional environment variables and volumes
version: '3.2' # e2e/cy-open.yml from repo # https://github.com/bahmutov/cypress-open-from-docker-compose services: cypress: # pass custom command to start Cypress otherwise it will use the entrypoint # specified in the Cypress Docker image. # also pass "--project <folder>" so that when Cypress opens # it can find file "cypress.json" and show integration specs # https://on.cypress.io/command-line#cypress-open entrypoint: cypress open --project /e2e environment: # get the IP address of the host machine and allow X11 to accept # incoming connections from that IP address # IP=$(ipconfig getifaddr en0) # /usr/X11/bin/xhost + $IP # then pass the environment variable DISPLAY to show Cypress GUI on the host system # DISPLAY=$IP:0 - DISPLAY volumes: # for Cypress to communicate with the X11 server pass this socket file # in addition to any other mapped volumes - /tmp/.X11-unix:/tmp/.X11-unix
To start in the interactive mode we need to pass both filenames to the docker
docker-compose -f docker-compose.yml -f cy-open.yml up --exit-code-from cypress
You should see the Test Runner and be able to run tests
Testing site on host
Let's consider another common situation: running the Test Runner inside a Docker container, while running the website on the host outside the container. First, start the website on the host machine
$ npm start # website is running at http://localhost:2222
Now start the Test Runner but instead of
localhost use a special Docker domain that points back at the host machine:
DISPLAY=$IP:0 docker run -it \ -v $PWD:/e2e \ -w /e2e \ -e DISPLAY \ --entrypoint cypress \ cypress/included:3.2.0 open --project . \ --config baseUrl=http://host.docker.internal:2222
Explanation of the "docker run" command line arguments: -it = interactive terminal -v $PWD:/e2e = map current folder to /e2e inside the container -w /e2e = set working directy to /e2e -e DISPLAY = pass environment variable DISPLAY to the container --entrypoint cypress = run "cypress" command with arguments AFTER Docker image name in our case they are "--project ." to point globally installed Cypress at the current working directory /e2e inside the container --config baseUrl=... = replace the default "baseUrl" with special domain pointing to the website running on the host machine
Running the Test Runner in a Docker container allows us to debug font and encoding issues. For example the problem of missing Chinese characters in the
cypress-documentation translation is only visible on CI or inside a Docker container.
- Cypress Docker documentation page
- Official cypress-docker-images
- “End-to-End Testing Web Apps: The Painless Way”
- cypress-open-from-docker-compose example
If you have any feedback for this approach or suggestions on how to make running Cypress even simpler for your team, please let us know. Open an issue in the cypress-io/cypress-docker-images repo or in the main cypress-io/cypress repo (and do not forget to give it a ⭐️!)
If you want to run Cypress Test Runner inside a Docker container, while the web application is running on the host machine, read how to do this in "Run Cypress included from Docker container"
You can use
cypress/included Docker image to run tests without installing any dependencies on GH Actions CI. See repository cypress-gh-action-included and its workflow file:
name: included on: [push] jobs: cypress-run: runs-on: ubuntu-latest # Docker image with Cypress pre-installed # https://github.com/cypress-io/cypress-docker-images/tree/master/included container: cypress/included:3.8.3 steps: - uses: actions/checkout@v1 - run: cypress run