This tutorial will help you set up a headless web testing project with Docker which will give you the following advantages:
- Continuous Integration: Docker is the only tool you need to install on the servers, no need to maintain complicated applications and versions.
- Easy to use within your team: Stop asking your mates to install the
Foo
and theBar
tools to make it work :-)
The following dependencies are only needed when setting up the project (e.g. using this tutorial). As soon as the project is set up, you do not need to request those dependencies to the rest of the developers (that's the magic of Docker). As well, you do not need them in CI machines:
If you want to skip the magic 🌟 of this article and start experimenting directly with the code 🔬, use the following commands:
# Clone project
git clone 'git@github.com:eridem/headless-web-testing-with-docker.git' 'my-testing-project'
cd 'my-testing-project'
# Build image
docker build . -t 'my-testing'
# Run tests (if Windows, use Powershell)
docker run -v "$(pwd)/output:/workdir/output" 'my-testing'
- Set up the project
- Running the tests with Docker
- Create a simple feature
- Questions & Wanna go advance
Create the Node.js application:
-
Open a terminal and write:
# Initialize the project: npm init # Answer the questions as following package name: (headless-web-testing-with-docker) version: (1.0.0) description: My awesome testing entry point: (index.js) test command: wdio git repository: eridem/headless-web-testing-with-docker keywords: test, web author: Miguel Angel Dominguez Coloma license: (ISC) MIT # Install webdriverio npm install webdriverio --save
-
Configure WebDriverIo executing the command:
# Set up the config of the webdriverio npm test -- config # Answer the questions as following ? Where do you want to execute your tests? On my local machine ? Which framework do you want to use? cucumber ? Shall I install the framework adapter for you? Yes ? Where are your feature files located? ./features/**/*.feature ? Where are your step definitions located? ./features/**/*.js ? Which reporter do you want to use? spec, junit ? Shall I install the reporter library for you? Yes ? Do you want to add a service to your test setup? selenium-standalone ? Shall I install the services for you? Yes ? Level of logging verbosity silent ? In which directory should screenshots gets saved if a command fails? ./output ? What is the base url? http://localhost
-
Modify the file
wdio.conf.js
, replacing the sectioncapabilities
where we will specify the use of the Chrome and Firefox browsers for our tests:... capabilities: [ { 'browserName': 'chrome', 'chromeOptions': { args: ['--headless', '--no-sandbox'] } }, { maxInstances: 5, browserName: 'firefox', "moz:firefoxOptions": { args: ['-headless'] } } ], ...
-
Modify the file
wdio.conf.js
, appending at the end of theconfig
section the following entry:... reporterOptions: { junit: { outputDir: './output/', outputFileFormat: function(opts) { // optional return `${opts.capabilities}.results-${opts.cid}.xml` } } }, ...
Create Docker files:
-
Add the file
.dockerignore
with the content:.git # Dependencies .node_modules # Logs npm-debug.log* yarn-debug.log* yarn-error.log* # Output output/*
-
Create the file
Dockerfile
with the content:FROM ubuntu:18.04 # Install machine dependencies RUN apt update \ && apt install -y unzip curl wget git make build-essential g++ openjdk-8-jdk \ && curl -sL https://deb.nodesource.com/setup_8.x | bash - \ && apt update \ && apt-get install -y nodejs \ && echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list \ && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ && apt update \ && apt install -y google-chrome-stable firefox # Working directory RUN mkdir -p /workdir/output WORKDIR /workdir # Install dependencies if any change COPY package.json package-lock.json ./ RUN npm install # Copy tests COPY . ./ # Execute tests ENTRYPOINT ["npm", "test"]
Create the following structure to store our tests and results:
+ features/
+ output/
Using Docker you do not need to install anything. You can run the following commands to run the tests:
# Build image
docker build . -t 'my-testing'
# Run tests (if Windows, use Powershell)
docker run -v "$(pwd)/output:/workdir/output" 'my-testing'
At this moment, it should display only empty results because we do not have tests:
pattern ./features/**/*.feature did not match any file
pattern ./features/**/*.feature did not match any file
-
Create the following file structure:
+ features/ + features/user-search/ + features/user-search/main.feature + features/user-search/main.js
-
Add the following content to the file
features/user-search/main.feature
:Feature: GitHub user search In order to find the repositories of an user in GitHub As user I want to have a search box to introduce my keywords Scenario: can search using my keywords Given the search GitHub page loaded When I introduce my search keywords for an user in the search box And I press enter in the search box Then I should obtain a list of repositories for that user
-
Add the following content to the file
features/user-search/main.js
:const { Given, When, Then } = require('cucumber') const { join } = require('path') const { screenshotPath } = require('../../wdio.conf').config const getScreenshotPath = (name) => join(screenshotPath, `${browser.desiredCapabilities.browserName}.${name}.png`) Given('the search GitHub page loaded', async () => { await browser.url('https://github.com/search') await browser.saveScreenshot(getScreenshotPath('GIVEN')) }) When('I introduce my search keywords for an user in the search box', async () => { await browser.setValue('[name=q]', 'user:eridem\n') await browser.saveScreenshot(getScreenshotPath('WHEN1')) }) When('I press enter in the search box', async () => { await browser.click('.btn') await browser.saveScreenshot(getScreenshotPath('WHEN2')) }) Then('I should obtain a list of repositories for that user', async () => { await browser.waitForExist('.codesearch-results') await browser.saveScreenshot(getScreenshotPath('THEN')) })
NOTE: the code should not create screenshots for each step, but we will do that to test our example.
-
Now that we have one feature, let's run the tests again with the commands:
# Build image docker build . -t 'my-testing' # Run tests (if Windows, use Powershell) docker run -v "$(pwd)/output:/workdir/output" 'my-testing'
Your terminal shows something like:
------------------------------------------------------------------
[chrome #0-0] Session ID: ad6c947b7dfbe912442fc6a60713d577
[chrome #0-0] Spec: /workdir/features/user-search/main.feature
[chrome #0-0] Running: chrome
[chrome #0-0]
[chrome #0-0] GitHub user search
[chrome #0-0]
[chrome #0-0] can search using my keywords
[chrome #0-0] ✓ the search GitHub page loaded
[chrome #0-0] ✓ I introduce my search keywords for an user in the search box
[chrome #0-0] ✓ I press enter in the search box
[chrome #0-0] ✓ I should obtain a list of repositories for that user
[chrome #0-0]
[chrome #0-0]
[chrome #0-0] 4 passing (4s)
[chrome #0-0]
------------------------------------------------------------------
[firefox #1-0] Session ID: 32d70e05-6a59-4de4-bf96-b62f90019c9e
[firefox #1-0] Spec: /workdir/features/user-search/main.feature
[firefox #1-0] Running: firefox
[firefox #1-0]
[firefox #1-0] GitHub user search
[firefox #1-0]
[firefox #1-0] can search using my keywords
[firefox #1-0] ✓ the search GitHub page loaded
[firefox #1-0] ✓ I introduce my search keywords for an user in the search box
[firefox #1-0] ✓ I press enter in the search box
[firefox #1-0] ✓ I should obtain a list of repositories for that user
[firefox #1-0]
[firefox #1-0]
[firefox #1-0] 4 passing (8s)
[firefox #1-0]
==================================================================
Number of specs: 2
8 passing (12.20s)
Wrote xunit report "chrome.results-0-0.xml" to [./output/].
Wrote xunit report "firefox.results-1-0.xml" to [./output/].
The folder output
should be filled with the results of your tests. You can use these output files in your CI tool or local read.