Skip to content

E2E testing using Docker, Cypress, VueJS, Golang and Postgres

Posted on

End-To-End testing is focused to test the entire application flow, simulating the user's interaction, ensuring that all components work as expected.
You probably have seen companies running a manual E2E testing in a staging environment. But what about to run automated E2E testing integrated with the CI process?
I will show up how I automated testing running the Cypress test runner for a single page application (SPA) using VueJS, Go and PostgreSQL, working on Docker containers, triggered by a Jenkins build. It's a good cheap solution for personal projects and startups.

Containers architecture

Docker creates a private network where containers can be connected together. I used Docker Compose to simplify the containers' creation and manages the dependencies. This is how I written the docker-compose.yml:

version: "3"
services:
  frontend:
    build:
      context: ./frontend
      args:
        - VUE_APP_API_URL=http://backend:11080
    container_name: frontend
    hostname: frontend
    ports:
      - "2015:2015"
    expose:
      - "2015"
    depends_on:
      - backend
    environment:
      - VUE_APP_API_URL=http://backend:11080

  backend:
    build: ./backend
    container_name: backend
    hostname: backend
    ports:
      - "17000:17000"
    expose:
      - "11080"
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgres://admin:password@db:5432/todos?sslmode=disable

  db:
    image: postgres:alpine
    container_name: db
    hostname: db
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=todos

  tests:
    build: ./tests
    container_name: tests
    depends_on:
      - frontend
      - backend
      - db
    environment:
      - DATABASE_URL=postgres://admin:password@db:5432/todos?sslmode=disable
      - CYPRESS_FRONTEND_URL=http://frontend:2015
    volumes:
      - "./tests/cypress:/home/node/tests/cypress"
      - "./tests/report:/home/node/tests/report"

Writing tests

Cypress is a test runner similar to Selenium/Webdriver. It is faster and doesn't need a graphical environment to run. Cypress has good integration with CI tools like Jenkins, CircleCI, Semaphore, Gitlab, and Travis.
In order to have repeatable tests it seems a good idea to have a test database populated with known data against which to write the tests. Populating this database is straightforward and resetting it could be as simple as dropping it at the end of the test or before to start the next. There are many ways to achieve it, developer's imagination is the limit. Here, I just create a shell script that uses the Postgres Command Line Tool (psql) to drop table and insert one registry.

Script resetdb.sh runned before each test:

echo "Reseting database..."
psql ${DATABASE_URL} <<EOF
DROP TABLE IF EXISTS tasks;
CREATE TABLE tasks (id SERIAL NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, description VARCHAR, done BOOL NOT NULL DEFAULT false, CONSTRAINT id_pk PRIMARY KEY (id));
INSERT INTO tasks (description) VALUES ('Create a TODO app as example of e2e testing');
EOF

A simple test written in Javascript:

describe('Todo tests', () => {

  // CYPRESS_FRONTEND_URL defined in docker-compose.yml
  const baseUrl = Cypress.env('FRONTEND_URL')

  beforeEach(() => {
    // Run the script resetdb.sh
    cy.exec('sh resetdb.sh')
  })

  context('When page is opened', () => {
    it('Creates a new task and add it in top of the list', () => {
      const task = 'todo test 1 cypress'

      cy.visit(baseUrl)
        .get('input[type="text"]').clear().type(task)
        .get('button.button').click()

      // Check if the saved task is on top
      cy.get('ul > li:first > .tasks__item__toggle')
        .should(($item) => {
          expect($item).to.contain(task)
        })
    })
  })
})

Configure Jenkins

I created a Freestyle Job with Execute shell property:

# Clear the Workspace
rm -rf * 2>/dev/null

# Clone the repository
git clone https://github.com/gustavohenrique/e2e-tests-using-docker-cypress-vuejs-golang-postgres.git

# Run the containers
cd e2e-tests-using-docker-cypress-vuejs-golang-postgres
docker-compose up --exit-code-from tests --force-recreate

I also configured the Job to public tests report in JUnit format in Post-build actions option:

# Report path
e2e-tests-using-docker-cypress-vuejs-golang-postgres/tests/report/output.xml

Conclusion

I run E2E or integration tests in containers in these scenarios:

  • I don't want to pay for a staging environment for my personal projects
  • I don't have a QA tester available in my team
  • I need to run tests in the CI process
  • I fell into a legacy project with no tests and I need feel safe before refactoring the code

Source code

The source code of the example is available in https://github.com/gustavohenrique/e2e-tests-using-docker-cypress-vuejs-golang-postgres.

comments powered by Disqus