Skip to content

Instantly share code, notes, and snippets.

@Jotschi
Last active October 31, 2016 11:28
Show Gist options
  • Save Jotschi/9be518c0661f18158644bfb4f80588af to your computer and use it in GitHub Desktop.
Save Jotschi/9be518c0661f18158644bfb4f80588af to your computer and use it in GitHub Desktop.

Integration Testing of a Dockerized Angular App

Protractor

We are using the Protractor testing framework in order to test our Gentics Mesh AngularJS Admin UI webapp. In this blogpost I will explain our setup and how we use docker to run the protractor tests.

Installation: npm install -g protractor

Selenium Node

Protractor uses Selenium in order to test the application.

The easiest way to setup a selenium node is to use the webdriver-manager tool but it is also possible to use selenium docker images.

Installation: npm install -g webdriver-manager

Our tests use the Firefox selenium node but it is also very easy to run tests using chrome.

The command docker run -d -p 4444:4444 selenium/standalone-firefox will spawn a new docker container which runs the selenium node.

Application Under Test

Next we need our AngularJS application. In our case this is Gentics Mesh. You can start the Gentics Mesh Demo Docker Container via docker run -p 8080:8080 gentics/mesh-demo. The started container will run the Gentics Mesh backend server and also serve the admin UI.

Tests

We have two test cases:

The login test will log in using the default demo credentials and verify that the demo project is being displayed.

/login-test.js

describe('mesh login test', function() {
  it('should be able to login', function() {
    browser.get('http://' + browser.params.meshHost + ':' + browser.params.meshPort + '/mesh-ui');

    element(by.model('vm.userName')).sendKeys('admin');
    element(by.model('vm.password')).sendKeys('admin');
    element(by.model('vm.password')).sendKeys('\n');

    var projectList = element.all(by.repeater('project in vm.projects'));
    expect(projectList.count()).toEqual(1);
    expect(projectList.get(0).getText()).toEqual('demo');
  });
});

The demo test will just access the demo endpoint which displays a simple AngularJS dummy webapp which displays the demo contents.

/demo-test.js

describe('mesh demo test', function() {
  it('should be list the demo', function() {
    browser.get('http://' + browser.params.meshHost + ':' + browser.params.meshPort + '/demo');
  });
});

Both tests use browser.params in order to access params we specify during protractor startup.

The main protractor configuration file is currently set up to run both tests using firefox.

/protractor.conf.js

exports.config = {
  specs: ['login-test.js', 'demo-test.js'],
  capabilities: {
    'browserName': 'firefox'
  }
};

Finally it is time to execute the protractor tests.

You can run the tests locally using a local selenium node which can be started using the webdriver-manager start command. Once the selenium node and your Gentics Mesh application is ready you can invoke the protractor tests:

protractor conf.js --params.meshHost localhost --params.meshPort 8080 --seleniumAddress http://localhost:4444/wd/hub

Combine all the things

combine all the things

Of course you could now glue ALL the pieces together using <$INSERT_YOUR_TOOL_HERE> here. In our case we use docker-compose and a little bit of bash for this.

I create a GitHub dummy project which contains all sources.

git clone git@github.com:gentics/protractor-docker-example.git
cd protractor-docker-example
./test.sh

docker-compose

Installation: described here

The /docker-compose.yml file contains three services:

  • tester: An image which contains the protractor tests. This will also execute these tests.
  • selenium: The selenium node which will be used by the tester service.
  • mesh: The application which will be tested. The selenium service must be able to access it.

The entrypoint value is customized in order to insert a bash script which will block execution until the needed service is ready. The /api/v1/admin/status endpoint is polled in order to verify that mesh is ready to accept requests. The selenium hub url is checked in a similar way.

version: '2'

services:
  tester:
    build: .
    entrypoint: bash -c "while ! timeout 1 wget -qO- http://selenium:4444/wd/hub > /dev/null ; do sleep 1; done; protractor --seleniumAddress http://selenium:4444/wd/hub --params.meshHost mesh --params.meshPort 8080 /app/protractor.conf.js"
  selenium:
    image: selenium/standalone-firefox
    ports:
      - "4444:4444"
    entrypoint: bash -c "while ! timeout 1 wget -qO- http://mesh:8080/api/v1/admin/status > /dev/null ; do sleep 1; done ; ./opt/bin/entry_point.sh"
  mesh:
    image: gentics/mesh-demo:latest
    ports:
      - "8080:8080"

A very simple docker file which adds the protractor configuration and test files to a new image which we will use to run our tests.

/Dockerfile

FROM jotschi/protractor

RUN mkdir -p /app
COPY *.js /app/

Test Runner

The test runner script is used to startup the docker-compose setup and wait until the tests have been executed. The test results will be printed to stdout and the exitcode of protractor will be passed along.

The following script is an adaptation of the test.sh script which was written by Harrison Harnisch. He also wrote an excellent blog post on integration testing using docker.

/test.sh

#!/bin/bash

# define some colors to use for output
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
# kill and remove any running containers
cleanup () {
  docker-compose kill
  docker-compose rm -f
}
# catch unexpected failures, do cleanup and output an error message
trap 'cleanup ; printf "${RED}Tests Failed For Unexpected Reasons${NC}\n"' HUP INT QUIT PIPE TERM
# build and run the composed services
docker-compose build && docker-compose up -d
if [ $? -ne 0 ] ; then
  printf "${RED}Docker Compose Failed${NC}\n"
  exit -1
fi
# wait for the test service to complete and grab the exit code
TEST_EXIT_CODE=`docker wait integrationtests_tester_1`
# output the logs for the test (for clarity)
docker logs --tail=all integrationtests_tester_1
# inspect the output of the test and display respective message
if [ -z ${TEST_EXIT_CODE+x} ] || [ "$TEST_EXIT_CODE" -ne 0 ] ; then
  printf "${RED}Tests Failed${NC} - Exit Code: $TEST_EXIT_CODE\n"
else
  printf "${GREEN}Tests Passed${NC}\n"
fi
# call the cleanup fuction
cleanup
# exit the script with the same code as the test service code
exit $TEST_EXIT_CODE

Finally you can run ./test.sh which will set up your docker-compose environment and execute the tests.

./test.sh 
mesh uses an image, skipping
selenium uses an image, skipping
Building tester
Step 1 : FROM jotschi/protractor
 ---> ffd0a8bd68a6
Step 2 : RUN mkdir -p /app
 ---> Using cache
 ---> b22bae66ffa7
Step 3 : COPY *.js /app/
 ---> Using cache
 ---> bacc34b8df2d
Successfully built bacc34b8df2d
Creating integrationtests_mesh_1
Creating integrationtests_selenium_1
Creating integrationtests_tester_1
[11:50:41] I/hosted - Using the selenium server at http://selenium:4444/wd/hub
[11:50:41] I/launcher - Running 1 instances of WebDriver
Started
..


2 specs, 0 failures
Finished in 4.84 seconds
[11:50:52] I/launcher - 0 instance(s) of WebDriver still running
[11:50:52] I/launcher - firefox #01 passed
Tests Passed
Killing integrationtests_selenium_1 ... done
Killing integrationtests_mesh_1 ... done
Going to remove integrationtests_tester_1, integrationtests_selenium_1, integrationtests_mesh_1
Removing integrationtests_tester_1 ... done
Removing integrationtests_selenium_1 ... done
Removing integrationtests_mesh_1 ... done

https://unsplash.com/photos/TtN_obfWlGw

https://unsplash.com/@wesleycaribe Wesley Caribe via unsplash

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment