Configure Cypress to wait the Java backend in a Docker based CI

Updated: 2023-06-27

The main (pain-)points and solutions here are:

  1. Create a Docker compose that doesn't require to modify the existing modules (backend and frontend), the Cypress Docker Compose configuration is all in the same directory.
  2. The Cypress tests don't have to go in TIMEOUT, they have to wait that the container with the server starts before to launch the tests.
  3. We want to publish the results with the JUnit format to be integrated with an external monitoring platform.
  4. The code has to be minimalistic, as usual.
  5. The solution is part of an automatic Pipeline (in Azure)

Our goal is to create a Docker Compose configuration to deploy and test our Angular/Java application using Cypress.

In the real world we are deploying this configuration in Azure.

Docker Compose

For our example we use the same structure of our full-stack application: https://marmo.dev/angular-with-java

We add an e2e directory in our SpringAngularDemo. The goal is to confine the e2e tests in this directory and don't add these tests or configuration in the frontend or backend modules of the webapp.

directory structure cypress

The docker compose file declare the 2 images that will be created, 1 for the webapp (backend + frontend) and 1 for the cypress tests.

version: '3' 
services: 
  webapp: 
    build: 
      dockerfile: ../e2e/delivery-dev-e2e.Dockerfile 
      context: ../delivery 
    container_name: webapp 
    restart: always 
    healthcheck: 
      test: ["CMD-SHELL", "curl -f http://webapp:8080/actuator/health || exit 1"] 
      interval: 30s 
      timeout: 20s 
      retries: 10 
  e2e: 
    container_name: cypress-tests 
    depends_on: 
      webapp: 
        condition: service_healthy 
    environment: 
      - CYPRESS_baseUrl=http://webapp:8080 
    build: 
      dockerfile: e2e.Dockerfile 
      context: . 
    volumes: 
      - ./results:/target/cypress/results 
 
    command: 
      - npx cypress run 

Two points are particularly important here:

  1. dockerfile: ../e2e/delivery-dev-e2e.Dockerfile allows us to use the delivery context (a different module) with our local dockerfile.

This allows us to don't pollute the backend or delivery directory with configuration files related to the cypress tests.

  1. healthcheck is a feature of docker that check if a container is healthy (started) according to a criteria that we can define.

The risk with a webapp and cypress is that the instance of cypress starts before the webapp server ends the initialization. Cypress call the home page and the server doesn't answer yet. Cypress tries few times and it stops with an error because the home page is not available.

Healthcheck ... wainting the start of the Java application

What we are doing with healthcheck is declaring the webapp container not healty until when Spring Boot doesn't answer OK.
To do this we have to activate actuator in Spring Boot.

Docker will monitor the url we defined as test and when this will answer with a 200 then Docker will start the Cypress tests.

The cypress container will be initialized only when webapp is healthy.

depends_on: 
      webapp: 
        condition: service_healthy 

Docker files

Our Docker files are standard.

For the delivery (or webapp):

FROM eclipse-temurin:17-jre 
COPY ./target/[webapp-name-*].jar /opt/webapp/webapp-name.jar 
CMD ["java", "-jar", "/opt/webapp/webapp-name.jar"] 

For the Cypress docker image:

FROM cypress/included:12.7.0 
 
COPY cypress.config.js . 
ADD cypres ./cypress 
COPY package.json . 
COPY package-lock.json . 
 
RUN npx cypress verify 

Cypress configuration

const {defineConfig} = require('cypress') 
 
module.exports = defineConfig({ 
    chromeWebSecurity: false, 
    reporter: 'junit', 
    reporterOptions: { 
        mochaFile: '/target/cypress/results/results-[hash].xml', 
        toConsole: false 
    }, 
    e2e: { 
        baseUrl: 'http://webapp:8081' 
    } 
}) 

In this configuration we have 2 particularly important parameters.

  1. chromeWebSecurity: false mitigates/avoids the TIMEOUT CypressError: Timed out retrying: cy.wait() timed out waiting 60000ms for the 1st request to the route.
  2. reporter: 'junit' allows to save the results in junit format and integrate the results in external tests platforms.

You could be interested in

Right click context menu with Angular

Right click custom menu inside dynamic lists with Angular Material
2020-05-31

Enums in Angular templates

How to use enum in the html template
2019-01-21
WebApp built by Marco using SpringBoot 3.2.4 and Java 21, in a Server in Switzerland