Greetings to all Cypress enthusiasts! This article describes the benefits of using Docker in Cypress testing, discusses in detail the current official Cypress images, and outlines the mechanism for building custom Docker images to run Cypress tests in Docker containers deployed based on these images. Content Overview Firstly, a few words about Cypress Benefits of using Docker in Cypress testing Briefly, about official Cypress images A short dive into a test project Running tests in Docker containers Final thoughts Firstly, a few words about Cypress Cypress is a JavaScript-based end-to-end testing tool designed for modern web test automation. Cypress has become a popular end-to-end web application testing tool due to its powerful features, user-friendly interface, and fast test execution. There are many pros and cons regarding the strength and weaknesses of the most popular web application testing frameworks today (Cypress, Playwright, Selenium, Puppeteer, etc.), but my own preference is Cypress, which I have been using on a daily basis for several years now. To my mind, Cypress is a real game changer in end-to-end and component testing and it grows at a rapid pace. Among the many benefits of Cypress, I would like to emphasize the high quality of its , as well as the master classes that the Cypress development team conducts and publishes in the public domain, and also a friendly and responsive community. Honestly, and as you may have noted from my previous articles I’m a big fan of that wonderful tool! documentation Benefits of using Docker in Cypress testing In modern , setting up and maintaining a test environment can often become a tedious task, especially when dealing with multiple dependencies and their configurations, different operating systems, libraries, tools, and versions. Often in practice, we can encounter dependency conflicts, inconsistencies in environments, limitations in scalability and reproduction of errors that occur, etc., which ultimately leads to unpredictable and unreliable test results. Using Docker goes a long way in preventing most of these problems. automated testing Docker is an open-source service for packaging, deploying, and running applications in an isolated containerized environment. Today, it is difficult to imagine the development of any web application without the participation of Docker. The usage of Docker containerization technologies brings a bunch of serious advantages for testing web applications, such as ensuring a for running tests in each new run; on which the tests are run; and of test results when they are run in different environments, etc. consistent and isolated environment independence from the parameters and settings of the system reliability, reproducibility, predictability In particular, using Docker in Cypress testing can be very useful for several reasons: It ensures that Cypress tests run in an because the container in which the tests are run is self-contained from other containers and from the external environment. In this case, the tests are essentially independent of what is outside the container, which ensures the . For example, in the case of deploying containers with tests locally, this means that the absence of Node.js, Cypress, any browser, or certain versions of them on the host computer will not become an obstacle to running Cypress tests. isolated test environment reliability and continuity of the tests in each new run Allows applications and Cypress tests to be run locally on different host machines, as well as deployed to CI/CD pipelines and cloud infrastructure by providing a stable and consistent test environment. When moving a Docker image from one server to another, containers with the application itself and tests will work the same regardless of the operating system used, the presence of Node.js, Cypress, browsers, etc. This ensures . reproducibility and predictability when running Cypress tests across different systems — on the servers that run the containers. Docker allows us to quickly deploy the containerized environment to run Cypress tests, so we do not need to install operating system dependencies, necessary browsers, and test frameworks every time. Frees us from having to install any dependencies outside of containers , embedding and deploying tests in various CI/CD pipelines, cloud services, etc., eliminating the need to troubleshoot compatibility issues during installation, reconfiguration, etc. In addition, Docker allows us to , for example, from a tester’s laptop to a stage or production environment. Significantly simplifies the integration of Cypress tests into the web application development process safely move tests between software deployment environments by reducing the total time for test runs. This is achieved through scaling, i.e. increasing the number of containers, running Cypress tests in different containers in parallel, parallel cross-browser testing capabilities using Docker Compose, etc. Speeds up the testing process Briefly about official Cypress images As of today, there are 4 hosted in the public image repository — , as well as in the corresponding repository of Cypress.io on GitHub: official Cypress Docker images Docker Hub “cypress-docker-images” cypress/factory cypress/base cypress/browsers cypress/included The Docker repositories of these images include their various versions and tags. The number of downloads of each image (except ) at the time of writing was more than , which is quite impressive. cypress/factory 50 million Now let’s see how each of these images can be interacted with in order to run Cypress tests in containers created from them. It should be noted that often these containers are not generated directly from the four specified images, but based on , which in turn are built . This is possible because Docker allows us to reuse official images as a and build custom images based on them by adding the necessary additional layers. custom images from official images base layer A short dive into a test project To demonstrate running tests in Docker containers, I’ll use “Cypress-Docker” — the simplest project with Cypress tests that I have to test my blog on Medium. “Testing with Cypress” The project already has some dependencies installed — and : Cypress 12.12.0 Typescript 5.0.4 and there is also one file with a test suite of three trivial tests for the blog’s homepage: spec.cy.ts By running these tests in mode, we are convinced of the successful execution of the tests: headless Here we should pay attention to the environment settings when running tests locally on the computer — , and browser . Next, we will make sure that when running tests inside Docker containers, these settings will change depending on the content of the Docker images used. I also note that Docker Desktop is already installed locally. Node.js 19.8.1, Cypress 12.12.0 Electron 106 Running tests in Docker containers Now let’s look at how we can run our Cypress tests inside Docker containers using each of the official Cypress Docker images. CYPRESS/INCLUDED I would like to start with this image because, unlike the others, it includes . This makes available to use out of the box without the need to add additional layers. The image allows us to create containers and run Cypress tests in them using just single command. all the dependencies of the operating system, pre-installed Cypress, and some browsers cypress/included To download an image from the Docker Hub, just run the following command in the terminal: > docker pull cypress/included:12.12.0 It’s important to note here that it’s a good idea to always add a specific image rather than relying on the default ( ) tag name because the latest version changes as new versions of the image are released. In the case of , the preinstalled inside it, for example, in my case, it is . Thus, if we do not specify the image version, then there is a chance that the tests will be run in different versions of Cypress since the containers for running the tests will be generated from images with different tags. tag latest cypress/included tag name corresponds to the version of Cypress 12.12.0 (stable) The presence of the specified image after it has finished downloading can be checked with a simple command: > docker images The downloaded image is shown first in the list of downloaded images: cypress/included To find out what this image contains, just execute the following command: > docker run -it --entrypoint=cypress cypress/included:12.12.0 info As a result, we get information about the version of Node.js preinstalled in the image, Cypress, three browsers, as well as other characteristics: Next, to run our Cypress tests in a container, we need to initiate the creation of a new container from the downloaded image with the command below: > docker run -it -v $PWD:/e2e -w /e2e cypress/included:12.12.0 With this command, we have created a Docker container, which essentially means a . Let’s take a look at the structure of used command: writable layer on top of existing image layers means to create and run a container based on the specified image docker run — interactive terminal -it — map the contents of the directory inside the container to the contents of the current project directory, where is the absolute path to the current directory ( of the project), is the path to the directory inside the container -v $PWD:/e2e $PWD root /e2e — set the working directory inside the container, where is the to the specified directory -w /e2e /e2e path — an indication of the image on the basis of which the container will be created. cypress/included:12.12.0 This command will create a Docker container in which Cypress will run tests on the built-in Electron browser. It is important to note that the image includes instruction, which automatically starts Cypress inside the container created from this image. cypress/included ENTRYPOINT ["cypress" "run"] If desired, it is possible to supplement the command with flag to specify a specific name for the created container, as well as flag to automatically delete the container after it is stopped: --name --rm > docker run -it -v $PWD:/e2e -w /e2e --name cypress-tests --rm cypress/included:12.12.0 At the same time, any additional arguments can be passed after the image name , similar to how we run Cypress in headless mode without using Docker. So, we can specify a specific browser or spec file to run, configure recording parameters, run tests in parallel, etc. For example, to run tests in any of the browsers preinstalled in the image ( ), it is enough to add to the command flag specifying the name of the browser: to control the behavior of Cypress inside Docker container Chrome, Edge, Firefox -b > docker run -it -v $PWD:/e2e -w /e2e cypress/included:12.12.0 -b chrome Or if we assume that initially, the project has several files, then to run a specific file/files, we should pass to the command flag specifying the absolute path to the required file/files: spec -s > docker run -it -v $PWD:/e2e -w /e2e cypress/included:12.12.0 -s cypress/e2e/spec.cy.ts Now that we know all those commands, let’s create a container and run our tests inside it using Chrome browser: And voila — we can now see that the specified test environment settings ( ) match the characteristics of the image content while being different from the settings when we initially ran the tests locally. Thus, it took . Node.js 18.16.0, Cypress 12.12.0, Chrome 113 cypress/included only single command to run the tests inside created Docker container Running tests multiple times in the same container It is important to note that each time we run Cypress tests based on the command, a new container will be created and run. To avoid this and be able , we need to connect to a process inside the container to control the running of tests from the container. To do this, it is enough to supplement the used command with flag with argument: docker run … to run tests multiple times inside the same container --entrypoint /bin/bash > docker run -it -v $PWD:/e2e -w /e2e --entrypoint=/bin/bash cypress/included:12.12.0 As a result, we have the ability to control the re-running of tests from the container using command, or to execute any other commands, for example, view files and folders inside the container using the standard command. cypress run ls -la In addition to the above way of running tests using image, we can build our own custom image based on it using . However, in the case of it often doesn’t make much practical sense (which can’t be said about other images), since this image already includes all the necessary dependencies of the test environment. Moreover, as mentioned earlier, already contains instruction, so there is no need to explicitly define the command to run Cypress tests in . cypress/included Dockerfile cypress/included cypress/included ENTRYPOINT ["cypress" "run"] Dockerfile Next, let’s look at another interesting official Cypress image — . cypress/browsers CYPRESS/BROWSERS This image includes . However, it does not contain preinstalled Cypress, which is similar to , the main difference is that in the latter we can . all operating system dependencies and some browsers cypress/factory optionally configure certain versions of browsers and operating system dependencies, as well as add Cypress of the desired version Each image is named according to the following : cypress/browsers general pattern cypress/browsers:node-<full Node version>-chrome<Chrome version>-ff-<Firefox version>-edge-<Edge version> This is a complete template, while in some versions of the image, there may be only one or two browsers from Chrome, Firefox, and Edge in various combinations. Let’s create a custom image based on . In doing so, I will deliberately use the current penultimate version of the base image to demonstrate the difference in the version of the browser used. cypress/browsers As you know, Docker can build images automatically by reading the instructions given in . In this case, for building the custom image will look like this: Dockerfile Dockerfile First, instruction defines a base image, all of whose dependencies and configurations will be included in the generated image. In particular, these are , as well as pre-installed browsers — , and . The base image will be picked up from Docker Hub. FROM Node.js 18.16.0 Chrome 112.0.5615.121–1, Firefox 112.0.1 Edge 112.0.1722.48–1 At the next step, , a working directory is created, in which all subsequent commands will be executed. If such a directory does not exist, this instruction will create it. WORKDIR /tests Next, the files and folders specified in command will be copied from the current project directory (in which is located) to the working directory inside the resulting image. It’s worth noting that I’m not copying the entire contents of the project, as it’s obviously not necessary. In particular, there is no point in copying the directory with the dependencies already installed, as they depend on the test environment settings. Also, to avoid copying, we can create a file by explicitly specifying inside it. COPY Dockerfile node-modules .dockerignore node-modules Then instruction executes commands during the image build process. In our case, it’s a command that installs all the necessary dependencies in the working directory, and in particular Cypress will be installed at this stage. RUN At the last step, the command to run Cypress is specified in , which will be executed inside the containers generated from this image. It is noteworthy that the instruction is written in exec form. ENTRYPOINT Let’s initiate an image build based on the instructions above by running the command: > docker build -t my-cypress-browsers:1.0.0 . where is the custom image build command docker build — an addition of arbitrary name and tag for the created image -t my-cypress-browsers:1.0.0 means that we are referring to the Dockerfile location as the Docker build context. . As we can see, the successful building of the resulting image ended with message: FINISHED Let’s check the existence of the created image. We can see that the built image is listed first in the list of available images: Next, from the created image, we will generate a container and invoke Cypress inside it to execute tests in the Chrome browser: > docker run -it my-cypress-browsers:1.0.0 -b chrome As we can see, our tests are automatically run in an environment with the Node.js and Chrome versions specified in the base image: Next, let’s go over one more image — . cypress/base CYPRESS/BASE This image contains . Due to this, is significantly (almost 2 times) smaller than the and images, which looks very attractive if there is no need to use some of the browsers. As stated in the image description, a it is built on. all the operating system dependencies necessary to run Cypress but does not contain Cypress itself and pre-installed browsers cypress/base cypress/included cypress/browsers specific image tag corresponds to the version of Node.js or the operating system To build our own image based on let’s create a new with the following instructions: cypress/base Dockerfile Here instruction defines as the base layer, where the tag name corresponds to the version preinstalled in the Node.js image. In this case, as in the previous case, I specify the penultimate version of the base image to demonstrate the difference in the versions of Node.js used. FROM cypress/base:18.15.0 18.15.0 At the next step, as before, a working directory is created to run all subsequent commands inside it. The instruction specifies the files and folders to be copied to the working directory within the resulting image. The command installs the necessary dependencies to the working directory. At the final stage, a command is specified to run tests inside containers generated from this image. COPY RUN Let’s set the name and tag for the resulting image — and build it with the already-known command: my-cypress-base:1.0.0 > docker build -t my-cypress-base:1.0.0 . Great, successful build of the image ended with message: FINISHED As before, let’s check if the built image exists. We can see that the image is listed first among the available images: my-cypress-base As mentioned earlier, due to the lack of browsers, the created image has a significantly (more than 2 times) smaller size than our previous images. Next, we will generate a container based on the created image and run Cypress tests inside it: > docker run -it my-cypress-base:1.0.0 Now we have verified that the tests run in the pre-installed browser. And as expected the specified version of corresponds to the tag name of the base image and differs from the previously used version ( ): Electron 106 Node.js — 18.15.0 18.16.0 It is important to note that if we try to run the tests in another browser, for example, by adding flag to the initial command, then despite the fact that the corresponding container will be generated, the tests will obviously not run due to the absence of the Chrome browser: -b chrome As stated in the text of the error, the only available browser is Electron, which is reasonable since the base image . cypress/base does not contain any pre-installed browsers Well, now let’s dive into the last of the official images — . cypress/factory CYPRESS/FACTORY This image appeared relatively recently, the first version of the image with the tag name was uploaded to Docker Hub about 4 months ago. allows building custom images with specific versions of: 1.0.0 Cypress/factory node yarn chrome firefox edge cypress As stated in the instructions for this image, using : cypress/factory provides the following benefits Freedom to choose which versions to test against. No need to wait on an official release to test the latest version of a browser. Smaller docker sizes especially when not including unused browsers. Easily test multiple browser versions. Reduced maintenance and pull requests for the cypress-docker repo. Ability to offer more variations of docker containers at low cost. Indeed, one of the main advantages of is the ability to customize the contents of the image, which allows, if necessary, to . Required versions of Node.js, Cypress, and browsers can be passed as when building the image. cypress/factory significantly reduce its size arguments Suppose we plan to build an image, including the most up-to-date versions of Node.js, as well as Chrome, Edge, and Firefox browsers. At the moment it is: Node.js 20.1.0 Chrome 113.0.5672.92–1 Edge 113.0.1774.42–1 Firefox 113.0 In this case, we should pass the specified parameters as to build the custom image. There are to declare them: arguments different ways as instructions in Dockerfile inside the image build command with flag --build-arg using and specifying them in the file Docker Compose docker-compose.yml By the way, if we don’t pass any arguments at all when creating the image, then by default the generated image will contain only Node.js. Let’s first look at the option of specifying arguments in . To do this, let’s create at the root of our project with the following content: Dockerfile Dockerfile As we can see, before instruction, the arguments for building the image are declared. is used to set build-time variables with key and value. In our case, it defines the required versions of Node.js and browsers. It’s important to note that the exact version must be used, no wildcards or shorthands are supported. FROM ARG ARG Further instruction states that the base image for creating a custom image is . FROM cypress/factory In the next step, the working directory is created inside the resulting image for executing all subsequent commands. WORKDIR /tests Then as in previous cases, instruction specifies the files and folders from the current project folder (which contains ) to be copied to . COPY Dockerfile WORKDIR Next, instruction will install the necessary dependencies specified in file. RUN package.json At the last stage, instruction, as already noted, specifies the command to run Cypress inside the containers that will be generated based on this image. ENTRYPOINT Now let’s create an image following the instructions above: > docker build -t my-cypress-factory:1.0.0 . After the image is built, we make sure that it is available: Now let’s initiate container creation from our resulting image and run Cypress tests inside it: > docker run -it my-cypress-factory:1.0.0 -b chrome As we can see, in the created container, the tests are automatically run in an environment with the versions of Node.js and Chrome browser specified in : Dockerfile Obviously, during test execution, we can also pass additional parameters to the container run command using . So, in the above command, the tests are run in the Chrome browser using the flag. to control the behavior of Cypress CLI flags -b To build a custom image without adding arguments to , we can run the following command: Dockerfile > docker build --build-arg NODE_VERSION='20.1.0' --build-arg CHROME_VERSION='113.0.5672.92-1' --build-arg EDGE_VERSION='113.0.1774.42-1' --build-arg FIREFOX_VERSION='113.0' -t my-cypress-factory:1.0.0 . By the way, this action overrides the values if they were also set in . ARG Dockerfile Despite the obvious flexibility in building custom images based on , it is possible to combine versions that are with each other within an image. For example, certain versions of Cypress may not support certain versions of Node.js. Obviously, this will allow us to build the image, but the containers generated from it will not work correctly. According to the developers, both the image itself and containers generated from it are intended for test use only and are not intended for use hosting services in a production environment. cypress/factory incompatible Final thoughts The official Cypress Docker images discussed in this article, as well as custom images created on their basis, of modern web applications and increase the reliability and efficiency of the testing process in general. Providing a and greatly simplifies the setup and maintenance of automated tests, and makes it easier to detect and troubleshoot issues that arise. greatly simplify the integration of Cypress tests into the development stable and consistent test environment reproducibility across different software deployment environments I hope this article was helpful for you to understand the basics of using containerization in Cypress testing. Additionally, it is worth noting that outside the article there were cases of running Cypress tests as part of several containers, parallel and cross-browser testing, which certainly deserves a separate article. That’s about it. If you found this useful, share it with a friend or community. Maybe there’s someone who will benefit from it as well. To continue your journey with me and get more information about testing with the awesome Cypress tool, you might be interested in subscribing to my blog and getting notified when there’s a new useful article. “Testing with Cypress” Dockerfiles used for this article, as well as the test project itself, can be found in the blog’s on GitHub. repository Thank you for your attention! Happy testing! Also published here.