Richard North's Blog

Better JUnit Selenium testing with Docker and Testcontainers

This is the third article in a series (1, 2) that explores integrated JUnit testing involving external components, supported by Testcontainers.

This post explores how Testcontainers can directly help improve our Selenium testing ruggedness and reduce some of the problems that I've observed on previous projects.

Some common problems and solutions #

A simple solution with Testcontainers selenium module #

Testcontainers allows fixed versions of Chrome and Firefox to be run inside of Docker containers, fully wired up to Selenium, VNC, and with automated video recording of tests.

The only dependency outside of the JUnit test suite is Docker.

Let's try a simple example - here's a very simple JUnit test that uses selenium:

Note: this example does not follow good practices such as use of page objects, but it's here for simple illustration using the raw Selenium API.

public class SeleniumContainerTest {

@Test
public void simplePlainSeleniumTest() {
RemoteWebDriver driver = // obtain a Webdriver instance somehow

driver.get("https://wikipedia.org");
WebElement searchInput = driver.findElementByName("search");

searchInput.sendKeys("Rick Astley");
searchInput.submit();

WebElement otherPage = driver.findElementByLinkText("Rickrolling");
otherPage.click();

boolean expectedTextFound = driver.findElementsByCssSelector("p")
.stream()
.anyMatch(element -> element.getText().contains("meme"));

assertTrue("The word 'meme' is found on a page about rickrolling", expectedTextFound);
}
}

Assuming we have the Testcontainers Selenium module on the classpath, the next step is to add a @Rule field to our test class as follows:

@Rule
public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer()
.withDesiredCapabilities(DesiredCapabilities.chrome())
.withRecordingMode(RECORD_ALL, new File("target"));

When we do this, Testcontainers will instantiate a new Docker container for each test method. We tell it to use Chrome, and Testcontainers will select a specific version-locked docker image (in this case selenium/standalone-chrome-debug:2.45.0).

Additionally, notice that we set a recording mode and a location for recordings to be placed. The mode can be RECORD_ALL or RECORD_FAILING - useful if we only want to inspect videos of failing test methods. Videos are recorded automatically for us, and saved as FLV files that can be played with VLC and other video players.

Next, in our test method (or perhaps in a @Before method) we obtain an instance of RemoteWebDriver, bound to our browser container:

RemoteWebDriver driver = chrome.getWebDriver();

This driver instance allows us to conduct our tests as we would normally! The following video was captured during execution of the tests, automatically:

https://www.youtube.com/watch?v=cX_iT2dfbq0

Our successful log output look a bit like:

21:22:23.392 [main] INFO  org.testcontainers.DockerClientFactory - Checking environment for docker settings
21:22:26.178 [main] INFO  org.testcontainers.DockerClientFactory - Could not initialize docker settings using environment variables: org.apache.http.conn.UnsupportedSchemeException: https protocol is not supported
21:22:26.180 [main] INFO  org.testcontainers.DockerClientFactory - Checking for presence of docker-machine
21:22:26.197 [main] INFO  org.testcontainers.utility.CommandLine - Executing shell command: `docker-machine ls -q`
21:22:26.538 [main] INFO  org.testcontainers.DockerClientFactory - Found docker-machine, and will use first machine defined (dev)
21:22:26.538 [main] INFO  org.testcontainers.utility.CommandLine - Executing shell command: `docker-machine status dev`
21:22:26.741 [main] INFO  org.testcontainers.utility.CommandLine - Executing shell command: `docker-machine ip dev`
21:22:27.437 [main] INFO  org.testcontainers.DockerClientFactory - Docker daemon IP address for docker machine dev is 192.168.99.100
21:22:30.480 [main] INFO  org.testcontainers.DockerClientFactory - Disk utilization in Docker environment is 22%21:22:31.147 [main] INFO  🐳 [selenium/standalone-chrome-debug:2.45.0] - Creating container for image: selenium/standalone-chrome-debug:2.45.0
21:22:31.340 [main] INFO  🐳 [selenium/standalone-chrome-debug:2.45.0] - Starting container with ID: b23b210ab508ed5714dd7ea22e09af696e303519a709ba4d09ac9a50b4e73242
21:22:31.587 [main] INFO  🐳 [selenium/standalone-chrome-debug:2.45.0] - Container selenium/standalone-chrome-debug:2.45.0 is starting: b23b210ab508ed5714dd7ea22e09af696e303519a709ba4d09ac9a50b4e73242
21:22:38.851 [pool-1-thread-1] INFO  🐳 [selenium/standalone-chrome-debug:2.45.0] - Obtained a connection to container (http://192.168.99.100:33215/wd/hub)
21:22:39.069 [main] INFO  🐳 [richnorth/vnc-recorder:latest] - Creating container for image: richnorth/vnc-recorder:latest
21:22:39.385 [main] INFO  🐳 [richnorth/vnc-recorder:latest] - Starting container with ID: 7067d9fecebad921308609e6416059219d25e9ba9a2aab53abf031d9b0a0ab1f
21:22:39.786 [main] INFO  🐳 [richnorth/vnc-recorder:latest] - Container richnorth/vnc-recorder:latest is starting: 7067d9fecebad921308609e6416059219d25e9ba9a2aab53abf031d9b0a0ab1f
21:22:40.100 [main] INFO  🐳 [richnorth/vnc-recorder:latest] - Container richnorth/vnc-recorder:latest started
21:22:40.101 [main] INFO  🐳 [selenium/standalone-chrome-debug:2.45.0] - Container selenium/standalone-chrome-debug:2.45.0 started
        ✔ The word 'meme' is found on a page about rickrolling
21:23:02.780 [main] INFO  org.testcontainers.containers.BrowserWebDriverContainer - Screen recordings for test simplePlainSeleniumTest(SeleniumContainerTest) will be stored at: target/recording-20160112-212302.flv
21:23:03.007 [main] INFO  🐳 [richnorth/vnc-recorder:latest] - Stopped container: richnorth/vnc-recorder:latest
21:23:03.096 [main] INFO  🐳 [richnorth/vnc-recorder:latest] - Removed container and associated volume(s): richnorth/vnc-recorder:latest
21:23:03.907 [main] INFO  🐳 [selenium/standalone-chrome-debug:2.45.0] - Stopped container: selenium/standalone-chrome-debug:2.45.0
21:23:03.965 [main] INFO  🐳 [selenium/standalone-chrome-debug:2.45.0] - Removed container and associated volume(s): selenium/standalone-chrome-debug:2.45.0

You can see the complete source code here.

Other useful methods on BrowserWebDriverContainer that we can use as we see fit:

open vnc://vnc:[email protected]:33212

Outcomes #

Conclusion #

I hope this post is informative about one potential technique for making selenium testing more rugged at the 'infrastructure' level. If it interests you, please give Testcontainers are try in your own projects.

A future post will hopefully look another awesome tool that addresses some of the problems of writing UI tests for modern webapps: Selenide.

← Home