SlideShare a Scribd company logo
Testcontainers - Geekout EE 2017 presentation
Who am I?
Richard North
• Lifelong geek
• Java/iOS/web/'devops' - 'full stack'
• Ex-consultant, now at Skyscanner
• UK and Japan
• Proud father of two!
@whichrich
Testing
Quick poll
Who writes tests?
Who writes unit tests?
Who writes integrated tests?
Code under test
Test suite
'External'/out-of-process dependencies
Who loves creating/maintaining
integrated tests?
Integrated tests
!
"You need to manually install Oracle XE on
your dev machine to run the tests..."
Weakly defined dependencies
"Oops Firefox upgraded itself - nobody can
run Selenium tests today"
Bit rot - variance with time
"Test B always fails if Test A ran before it"
Shared state - tests interfere with each other
"But it worked when we tested against the
mock!"
Mocks provide varying assurance of compatibility
"I can only run that test on CI. I can't connect
a debugger."
Testing capabilities vary through the pipeline
There has to be a better way
Testing Pyramid
Testcontainers - Geekout EE 2017 presentation
Just don't have any integrated
tests?
"2 Unit Tests, 0 Integration Tests"
Be pragmatic
Have as few integrated tests as we
can get away with
...
Have as few integrated tests as we
can get away with
and make them easier to work with
Testcontainers - Geekout EE 2017 presentation
Deployment and Infrastructure
Once upon a time...
"It'll take you three days to build an
environment"
— My first tech lead
- Slow, manual setup
- No dev/prod parity
- Expensive to do the right thing
Deployment and Infrastructure
!
Fast forward to the future
Testcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentation
"we can build an environment in seconds"
- quickly, cheaply
- always the same
- definition is version controlled
- Docker Hub - plentiful base images
Deployment
❤
Docker
What if..?
Docker for Testing?
Testcontainers
• Manage Dockerized external dependencies via a Java object facade
• JUnit integration - Starts/stops containers for each class/method
• Reliability:
• start from clean state
• isolated instances
• port randomisation
• tag-based versioning
• Java JUnit support; also Spock, Scala and Python wrappers/forks
Testcontainers project
• Initial versions mid 2015
• 36 contributors over time; Sergei Egorov (@bsideup) is the main co-maintainer
• Some users:
• ZeroTurnaround
• Spring Data
• Apache
• Zalando
• Alfalab
• Zipkin
• Others...!
Where can Testcontainers help me?
Supported test dependencies
Type Specialisations
GenericContainer Any image on Docker Hub (or private repo!)
Databases MySQL, PostgreSQL, MariaDB, Oracle XE,
DynamoDB
Selenium Chrome, Firefox
Docker Compose Anything in a Docker Compose file
Dockerfile / Dockerfile DSL Anything expressable in a Dockerfile
A simple example
Integrated tests involving a cache
public interface Cache {
void put(String key, String value);
String get(String key);
}
public class RedisBackedCache implements Cache {
// Uses Redis...
}
public class RedisBackedCacheTest {
private Cache cache;
@Before
public void setup() {
cache = new RedisBackedCache("localhost", 6379);
}
@Test
public void testGetAndSetAValue() {
cache.put("foo", "bar");
final String retrievedValue = cache.get("foo");
assertEquals("The retrieved value is the same as the inserted value", "bar", retrievedValue);
}
}
public class RedisBackedCacheTest {
@Rule
public static GenericContainer redis = new GenericContainer("redis:3.2.8");
private Cache cache;
@Before
public void setup() {
cache = new RedisBackedCache(???, ???);
}
@Test
public void testGetAndSetAValue() {
cache.put("foo", "bar");
final String retrievedValue = cache.get("foo");
assertEquals("The retrieved value is the same as the inserted value", "bar", retrievedValue);
}
}
public class RedisBackedCacheTest {
@Rule
public static GenericContainer redis = new GenericContainer("redis:3.2.8")
.withExposedPorts(6379);
private Cache cache;
@Before
public void setup() {
cache = new RedisBackedCache(redis.getContainerIpAddress(), redis.getMappedPort(6379));
}
@Test
public void testGetAndSetAValue() {
cache.put("foo", "bar");
final String retrievedValue = cache.get("foo");
assertEquals("The retrieved value is the same as the inserted value", "bar", retrievedValue);
}
}
What will Testcontainers do here?
• Automatic discovery of local docker environment
• Pull images or build from Dockerfile
• Start/stop container
• Wait for it to be ready (log string / listening port / protocol-
specific)
• Port mapping
• Clean up
What have we avoided?
• No need to install the dependency
• No need to keep it running, or make sure it's running
• No concern over version or configuration differences
• No differences between what runs on CI and locally
• No port clashes, no shared state unless we want it
What have we gained?
• Repeatability - locked version redis:3.2.8
• Debuggable locally - runnable in IDE
• Parallelizable
• Runs anywhere that Docker runs
'Anywhere Docker runs'
Automatic discovery:
• Docker for Mac and Docker for Windows
• Docker Machine
• Uses a running machine instance, or default
• Automatically starts up Docker Machine if needed
• Docker on Linux
• Cloud CI
• Travis CI
• Circle CI
• Docker in Docker
• ... or wherever DOCKER_HOST is set
Example 2
Database testing
A simple DAO API
public interface UsefulDao {
void putAThing(String name, SomeObject value);
SomeObject getAThingByJsonId(Integer id);
}
A corresponding test
public class UsefulDaoTest {
private UsefulDao dao;
@Before
public void setUp() throws Exception {
// Instantiate the DAO
// Connect
// Create schema and data
}
@Test
public void testPutAndGetByJsonIndex() throws Exception {
// INSERT and SELECT something
}
}
How can we test this with no
database?
Embedded database!
Our (fictitious) schema
CREATE TABLE THINGS ( name VARCHAR(255), data JSONB );
JSONB is a PostgreSQL data type - how can we test this?
• Embedded database?
• Run PostgreSQL through our build script?
• Hope that the developer/CI environment has the right version of
PostgreSQL?
• Give up, and don't use database features we can't easily test? !
• Don't test interactions with the DB, and hope that it works in prod? !!
• Can we use Testcontainers..?
Yes we can!
@Rule
public PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:9.6.2");
Access at test-time
postgres.getJdbcUrl(); // Unique URL for a container instance
postgres.getUsername();
postgres.getPassword();
Example 3
Selenium Webdriver testing
public class SeleniumTest {
private WebDriver driver;
@Before
public void setUp() throws Exception {
// Connect to remote selenium grid
// or start a local browser process (Headless? Real browser?)
}
@Test
public void testSimple() throws IOException {
...
}
}
public class SeleniumTest {
@Rule
public BrowserWebDriverContainer chrome =
new BrowserWebDriverContainer()
.withDesiredCapabilities(DesiredCapabilities.chrome());
private WebDriver driver;
@Before
public void setUp() throws Exception {
driver = chrome.getWebDriver();
}
@Test
public void testSimple() throws IOException {
...
}
}
driver.get("https://2017.geekout.ee/");
driver.findElement(
By.linkText("SCHEDULE"))
.click();
driver.findElement(
By.partialLinkText("TestContainers"))
.click();
driver.findElement(
By.linkText("Richard North"))
.click();
final String siteUrl = driver.findElement(
By.partialLinkText("testcontainers"))
.getText();
assertEquals("The right link is found",
"https://www.testcontainers.org/",
siteUrl);
Recording videos
@Rule
public BrowserWebDriverContainer chrome =
new BrowserWebDriverContainer()
.withDesiredCapabilities(DesiredCapabilities.chrome())
.withRecordingMode(RECORD_FAILING, new File("./target"));
Debug via VNC!
Set a breakpoint:
Get a VNC URL
chrome.getVncAddress() // e.g. "vnc://vnc:secret@localhost:32786"
Connect
$ open vnc://vnc:secret@localhost:32786
Recap so far
• Using an image from Docker Hub as a dependency
• Specialised database support
• Selenium testing
Example 4
Dockerfile build
Build a container image at test time
Allows:
• Running code in real, prod-like Docker image
• Create an image that's not available from a registry
• Parameterized builds - using a DSL
Doesn't require a separate phase for build/test pipeline
Build a container image at test time -
Dockerfile
FROM tomcat:8.5.15
COPY service.war /usr/local/tomcat/webapps/my-service.war
Build a container image at test time -
Dockerfile
@Rule
public GenericContainer server = new GenericContainer(
new ImageFromDockerfile()
.withFileFromFile("Dockerfile", new File("./Dockerfile"))
.withFileFromFile("service.war", new File("target/my-service.war")))
.withExposedPorts(8080);
@Test
public void simpleTest() {
// do something with the server - port is server.getMappedPort(8080));
}
Build a container image at test time - DSL
@Rule
public GenericContainer server = new GenericContainer(
new ImageFromDockerfile()
.withFileFromFile("service.war", new File("target/my-service.war"))
.withDockerfileFromBuilder(builder -> {
builder
.from("tomcat:8.5.15")
.copy("service.war", "/usr/local/tomcat/webapps/my-service.war")
.build();
}))
.withExposedPorts(8080);
@Test
public void simpleTest() {
// do something with the server - port is server.getMappedPort(8080));
}
Example 5
Docker Compose
docker-compose.backend.yml:
version: '2'
services:
db:
image: mongo:3.0.15
cache:
image: redis:3.2.8
search:
image: elasticsearch:5.4.0
Multiple containers as JUnit rules
One way?
public class SimpleSystemTest {
@ClassRule
public GenericContainer db = new GenericContainer("mongo:3.0.15");
@ClassRule
public GenericContainer cache = new GenericContainer("redis:3.2.8");
@ClassRule
public GenericContainer search = new GenericContainer("elasticsearch:5.4.0");
// ... tests ...
}
Using Docker Compose during a test
@Rule
public DockerComposeContainer backend = new DockerComposeContainer(new File("./docker-compose.backend.yml"))
.withExposedService("db", 27017)
.withExposedService("cache", 6379)
.withExposedService("search", 9200);
@Test
public void simpleTest() {
// obtain host/ports for each container as follows:
// backend.getServiceHost("db", 27017);
// backend.getServicePort("db", 27017);
// ...
}
Docker Compose in Testcontainers
• Unique, random, name prefix and isolated network - allows concurrent usage
One usage mode:
• Use docker-compose up -f ... during local dev (overrides file to expose
ports)
• Run tests concurrently via Testcontainers without stopping local
environment
• Seamless transition to CI - use Testcontainers
Summary
• Generic image container
• Specialised Database container
• Selenium containers with video recording and VNC debugging
• Building a Dockerfile
• Docker Compose
What's next?
Speed enhancements
• Startup containers in in advance
• Checkpoint-Restore In Userspace
'Version 2'
• API tidyup
• decouple from JUnit 4 and support other frameworks directly
Core elements as a library
• high-level Docker object API as a library, for more than just
testing usage
• planning collaboration with Arquillian Cube project team !
More things!
• Pumba (Chaos testing) - landing soon!
• ...
Conclusion
• Hopefully another useful tool for your testing toolbox
• Easy to use, powerful features for many scenarios
• Please try it out yourselves!
Thanks to
• Everyone who has contributed to the project
• ZeroTurnaround
• You!
testcontainers.org
github.com/testcontainers
@testcontainers
Thank you!
Testcontainers - Geekout EE 2017 presentation

More Related Content

What's hot (20)

PDF
Fallacies in Platform Engineering.pdf
LibbySchulze
 
PDF
Docker Basics
Eueung Mulyana
 
PPTX
Charles guide sharing
Vincent Chang
 
PPTX
Python Programming | JNTUK | UNIT 2 | Lecture 6 & 7 | Conditional & Control S...
FabMinds
 
PPTX
Introduction to Python.Net
Stefan Schukat
 
PDF
Working with Dynamic Content and Adding Templating engines, MVC
Knoldus Inc.
 
PDF
Crud tutorial en
forkgrown
 
PDF
Lets make a better react form
Yao Nien Chung
 
PDF
JUnit & Mockito, first steps
Renato Primavera
 
PPTX
JSON in Solr: From Top to Bottom - Alexander Rafalovitch, United Nations
Lucidworks
 
PPTX
Its time to React.js
Ritesh Mehrotra
 
PDF
What is integration testing
TestingXperts
 
PPT
Cucumber presentation
Akhila B
 
PPTX
React js - The Core Concepts
Divyang Bhambhani
 
PPT
Performance testing : An Overview
sharadkjain
 
PDF
History and Versions of Java Programming.pdf
JavaTpoint.Com
 
PDF
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
SangIn Choung
 
PPTX
在學校開機場把自己的學費賺回來!.pptx
HsiangMingHung
 
PDF
Introduction to jest
pksjce
 
PPTX
Docker introduction for the beginners
Juneyoung Oh
 
Fallacies in Platform Engineering.pdf
LibbySchulze
 
Docker Basics
Eueung Mulyana
 
Charles guide sharing
Vincent Chang
 
Python Programming | JNTUK | UNIT 2 | Lecture 6 & 7 | Conditional & Control S...
FabMinds
 
Introduction to Python.Net
Stefan Schukat
 
Working with Dynamic Content and Adding Templating engines, MVC
Knoldus Inc.
 
Crud tutorial en
forkgrown
 
Lets make a better react form
Yao Nien Chung
 
JUnit & Mockito, first steps
Renato Primavera
 
JSON in Solr: From Top to Bottom - Alexander Rafalovitch, United Nations
Lucidworks
 
Its time to React.js
Ritesh Mehrotra
 
What is integration testing
TestingXperts
 
Cucumber presentation
Akhila B
 
React js - The Core Concepts
Divyang Bhambhani
 
Performance testing : An Overview
sharadkjain
 
History and Versions of Java Programming.pdf
JavaTpoint.Com
 
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
SangIn Choung
 
在學校開機場把自己的學費賺回來!.pptx
HsiangMingHung
 
Introduction to jest
pksjce
 
Docker introduction for the beginners
Juneyoung Oh
 

Similar to Testcontainers - Geekout EE 2017 presentation (20)

PDF
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
FalafelSoftware
 
PDF
Breaking Dependencies to Allow Unit Testing
Steven Smith
 
PPTX
OWASP ZAP Workshop for QA Testers
Javan Rasokat
 
PDF
Cannibalising The Google App Engine
catherinewall
 
PDF
Building XWiki
Vincent Massol
 
PDF
Integration tests: use the containers, Luke!
Roberto Franchini
 
PPTX
Breaking Dependencies to Allow Unit Testing
Steven Smith
 
PDF
Oscon Java Testing on the Fast Lane
Andres Almiray
 
PPTX
JLove - Replicating production on your laptop using the magic of containers
Grace Jansen
 
PPTX
Easy Java Integration Testing with Testcontainers​
Payara
 
PPTX
JBCN_Testing_With_Containers
Grace Jansen
 
PDF
DevOps Odessa #TechTalks 21.01.2020
Lohika_Odessa_TechTalks
 
PPTX
Testing ASP.NET - Progressive.NET
Ben Hall
 
KEY
Unit Testing in SharePoint 2010
Chris Weldon
 
PPTX
Java script unit testing
Mats Bryntse
 
PDF
[KubeCon NA 2018] Telepresence Deep Dive Session - Rafael Schloming & Luke Sh...
Ambassador Labs
 
PDF
How to Build Your Own Test Automation Framework?
Dmitry Buzdin
 
ZIP
Browser-Based testing using Selenium
ret0
 
PDF
Testing the Enterprise layers, with Arquillian
Virtual JBoss User Group
 
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
FalafelSoftware
 
Breaking Dependencies to Allow Unit Testing
Steven Smith
 
OWASP ZAP Workshop for QA Testers
Javan Rasokat
 
Cannibalising The Google App Engine
catherinewall
 
Building XWiki
Vincent Massol
 
Integration tests: use the containers, Luke!
Roberto Franchini
 
Breaking Dependencies to Allow Unit Testing
Steven Smith
 
Oscon Java Testing on the Fast Lane
Andres Almiray
 
JLove - Replicating production on your laptop using the magic of containers
Grace Jansen
 
Easy Java Integration Testing with Testcontainers​
Payara
 
JBCN_Testing_With_Containers
Grace Jansen
 
DevOps Odessa #TechTalks 21.01.2020
Lohika_Odessa_TechTalks
 
Testing ASP.NET - Progressive.NET
Ben Hall
 
Unit Testing in SharePoint 2010
Chris Weldon
 
Java script unit testing
Mats Bryntse
 
[KubeCon NA 2018] Telepresence Deep Dive Session - Rafael Schloming & Luke Sh...
Ambassador Labs
 
How to Build Your Own Test Automation Framework?
Dmitry Buzdin
 
Browser-Based testing using Selenium
ret0
 
Testing the Enterprise layers, with Arquillian
Virtual JBoss User Group
 
Ad

Recently uploaded (20)

PDF
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
PDF
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
PPTX
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
PDF
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
PDF
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
PDF
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
PDF
Economic Impact of Data Centres to the Malaysian Economy
flintglobalapac
 
PPTX
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
PPTX
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
PPTX
AI in Daily Life: How Artificial Intelligence Helps Us Every Day
vanshrpatil7
 
PDF
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
PPTX
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
PDF
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
PDF
Brief History of Internet - Early Days of Internet
sutharharshit158
 
PDF
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
PPTX
Agile Chennai 18-19 July 2025 | Workshop - Enhancing Agile Collaboration with...
AgileNetwork
 
PPTX
Farrell_Programming Logic and Design slides_10e_ch02_PowerPoint.pptx
bashnahara11
 
PDF
OFFOFFBOX™ – A New Era for African Film | Startup Presentation
ambaicciwalkerbrian
 
PDF
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
PDF
The Future of Artificial Intelligence (AI)
Mukul
 
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
Economic Impact of Data Centres to the Malaysian Economy
flintglobalapac
 
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
AI in Daily Life: How Artificial Intelligence Helps Us Every Day
vanshrpatil7
 
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
Brief History of Internet - Early Days of Internet
sutharharshit158
 
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
Agile Chennai 18-19 July 2025 | Workshop - Enhancing Agile Collaboration with...
AgileNetwork
 
Farrell_Programming Logic and Design slides_10e_ch02_PowerPoint.pptx
bashnahara11
 
OFFOFFBOX™ – A New Era for African Film | Startup Presentation
ambaicciwalkerbrian
 
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
The Future of Artificial Intelligence (AI)
Mukul
 
Ad

Testcontainers - Geekout EE 2017 presentation

  • 2. Who am I? Richard North • Lifelong geek • Java/iOS/web/'devops' - 'full stack' • Ex-consultant, now at Skyscanner • UK and Japan • Proud father of two! @whichrich
  • 6. Who writes integrated tests? Code under test Test suite 'External'/out-of-process dependencies
  • 9. "You need to manually install Oracle XE on your dev machine to run the tests..." Weakly defined dependencies
  • 10. "Oops Firefox upgraded itself - nobody can run Selenium tests today" Bit rot - variance with time
  • 11. "Test B always fails if Test A ran before it" Shared state - tests interfere with each other
  • 12. "But it worked when we tested against the mock!" Mocks provide varying assurance of compatibility
  • 13. "I can only run that test on CI. I can't connect a debugger." Testing capabilities vary through the pipeline
  • 14. There has to be a better way
  • 17. Just don't have any integrated tests?
  • 18. "2 Unit Tests, 0 Integration Tests"
  • 20. Have as few integrated tests as we can get away with ...
  • 21. Have as few integrated tests as we can get away with and make them easier to work with
  • 24. Once upon a time...
  • 25. "It'll take you three days to build an environment" — My first tech lead
  • 26. - Slow, manual setup - No dev/prod parity - Expensive to do the right thing
  • 28. Fast forward to the future
  • 32. "we can build an environment in seconds" - quickly, cheaply - always the same - definition is version controlled - Docker Hub - plentiful base images
  • 35. Testcontainers • Manage Dockerized external dependencies via a Java object facade • JUnit integration - Starts/stops containers for each class/method • Reliability: • start from clean state • isolated instances • port randomisation • tag-based versioning • Java JUnit support; also Spock, Scala and Python wrappers/forks
  • 36. Testcontainers project • Initial versions mid 2015 • 36 contributors over time; Sergei Egorov (@bsideup) is the main co-maintainer • Some users: • ZeroTurnaround • Spring Data • Apache • Zalando • Alfalab • Zipkin • Others...!
  • 38. Supported test dependencies Type Specialisations GenericContainer Any image on Docker Hub (or private repo!) Databases MySQL, PostgreSQL, MariaDB, Oracle XE, DynamoDB Selenium Chrome, Firefox Docker Compose Anything in a Docker Compose file Dockerfile / Dockerfile DSL Anything expressable in a Dockerfile
  • 39. A simple example Integrated tests involving a cache
  • 40. public interface Cache { void put(String key, String value); String get(String key); } public class RedisBackedCache implements Cache { // Uses Redis... }
  • 41. public class RedisBackedCacheTest { private Cache cache; @Before public void setup() { cache = new RedisBackedCache("localhost", 6379); } @Test public void testGetAndSetAValue() { cache.put("foo", "bar"); final String retrievedValue = cache.get("foo"); assertEquals("The retrieved value is the same as the inserted value", "bar", retrievedValue); } }
  • 42. public class RedisBackedCacheTest { @Rule public static GenericContainer redis = new GenericContainer("redis:3.2.8"); private Cache cache; @Before public void setup() { cache = new RedisBackedCache(???, ???); } @Test public void testGetAndSetAValue() { cache.put("foo", "bar"); final String retrievedValue = cache.get("foo"); assertEquals("The retrieved value is the same as the inserted value", "bar", retrievedValue); } }
  • 43. public class RedisBackedCacheTest { @Rule public static GenericContainer redis = new GenericContainer("redis:3.2.8") .withExposedPorts(6379); private Cache cache; @Before public void setup() { cache = new RedisBackedCache(redis.getContainerIpAddress(), redis.getMappedPort(6379)); } @Test public void testGetAndSetAValue() { cache.put("foo", "bar"); final String retrievedValue = cache.get("foo"); assertEquals("The retrieved value is the same as the inserted value", "bar", retrievedValue); } }
  • 45. • Automatic discovery of local docker environment • Pull images or build from Dockerfile • Start/stop container • Wait for it to be ready (log string / listening port / protocol- specific) • Port mapping • Clean up
  • 46. What have we avoided? • No need to install the dependency • No need to keep it running, or make sure it's running • No concern over version or configuration differences • No differences between what runs on CI and locally • No port clashes, no shared state unless we want it
  • 47. What have we gained? • Repeatability - locked version redis:3.2.8 • Debuggable locally - runnable in IDE • Parallelizable • Runs anywhere that Docker runs
  • 48. 'Anywhere Docker runs' Automatic discovery: • Docker for Mac and Docker for Windows • Docker Machine • Uses a running machine instance, or default • Automatically starts up Docker Machine if needed • Docker on Linux • Cloud CI • Travis CI • Circle CI • Docker in Docker • ... or wherever DOCKER_HOST is set
  • 50. A simple DAO API public interface UsefulDao { void putAThing(String name, SomeObject value); SomeObject getAThingByJsonId(Integer id); }
  • 51. A corresponding test public class UsefulDaoTest { private UsefulDao dao; @Before public void setUp() throws Exception { // Instantiate the DAO // Connect // Create schema and data } @Test public void testPutAndGetByJsonIndex() throws Exception { // INSERT and SELECT something } }
  • 52. How can we test this with no database? Embedded database!
  • 53. Our (fictitious) schema CREATE TABLE THINGS ( name VARCHAR(255), data JSONB );
  • 54. JSONB is a PostgreSQL data type - how can we test this? • Embedded database? • Run PostgreSQL through our build script? • Hope that the developer/CI environment has the right version of PostgreSQL? • Give up, and don't use database features we can't easily test? ! • Don't test interactions with the DB, and hope that it works in prod? !! • Can we use Testcontainers..?
  • 55. Yes we can! @Rule public PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:9.6.2"); Access at test-time postgres.getJdbcUrl(); // Unique URL for a container instance postgres.getUsername(); postgres.getPassword();
  • 57. public class SeleniumTest { private WebDriver driver; @Before public void setUp() throws Exception { // Connect to remote selenium grid // or start a local browser process (Headless? Real browser?) } @Test public void testSimple() throws IOException { ... } }
  • 58. public class SeleniumTest { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() .withDesiredCapabilities(DesiredCapabilities.chrome()); private WebDriver driver; @Before public void setUp() throws Exception { driver = chrome.getWebDriver(); } @Test public void testSimple() throws IOException { ... } }
  • 59. driver.get("https://2017.geekout.ee/"); driver.findElement( By.linkText("SCHEDULE")) .click(); driver.findElement( By.partialLinkText("TestContainers")) .click(); driver.findElement( By.linkText("Richard North")) .click(); final String siteUrl = driver.findElement( By.partialLinkText("testcontainers")) .getText(); assertEquals("The right link is found", "https://www.testcontainers.org/", siteUrl);
  • 60. Recording videos @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() .withDesiredCapabilities(DesiredCapabilities.chrome()) .withRecordingMode(RECORD_FAILING, new File("./target"));
  • 61. Debug via VNC! Set a breakpoint: Get a VNC URL chrome.getVncAddress() // e.g. "vnc://vnc:secret@localhost:32786" Connect $ open vnc://vnc:secret@localhost:32786
  • 62. Recap so far • Using an image from Docker Hub as a dependency • Specialised database support • Selenium testing
  • 64. Build a container image at test time Allows: • Running code in real, prod-like Docker image • Create an image that's not available from a registry • Parameterized builds - using a DSL Doesn't require a separate phase for build/test pipeline
  • 65. Build a container image at test time - Dockerfile FROM tomcat:8.5.15 COPY service.war /usr/local/tomcat/webapps/my-service.war
  • 66. Build a container image at test time - Dockerfile @Rule public GenericContainer server = new GenericContainer( new ImageFromDockerfile() .withFileFromFile("Dockerfile", new File("./Dockerfile")) .withFileFromFile("service.war", new File("target/my-service.war"))) .withExposedPorts(8080); @Test public void simpleTest() { // do something with the server - port is server.getMappedPort(8080)); }
  • 67. Build a container image at test time - DSL @Rule public GenericContainer server = new GenericContainer( new ImageFromDockerfile() .withFileFromFile("service.war", new File("target/my-service.war")) .withDockerfileFromBuilder(builder -> { builder .from("tomcat:8.5.15") .copy("service.war", "/usr/local/tomcat/webapps/my-service.war") .build(); })) .withExposedPorts(8080); @Test public void simpleTest() { // do something with the server - port is server.getMappedPort(8080)); }
  • 70. Multiple containers as JUnit rules One way? public class SimpleSystemTest { @ClassRule public GenericContainer db = new GenericContainer("mongo:3.0.15"); @ClassRule public GenericContainer cache = new GenericContainer("redis:3.2.8"); @ClassRule public GenericContainer search = new GenericContainer("elasticsearch:5.4.0"); // ... tests ... }
  • 71. Using Docker Compose during a test @Rule public DockerComposeContainer backend = new DockerComposeContainer(new File("./docker-compose.backend.yml")) .withExposedService("db", 27017) .withExposedService("cache", 6379) .withExposedService("search", 9200); @Test public void simpleTest() { // obtain host/ports for each container as follows: // backend.getServiceHost("db", 27017); // backend.getServicePort("db", 27017); // ... }
  • 72. Docker Compose in Testcontainers • Unique, random, name prefix and isolated network - allows concurrent usage One usage mode: • Use docker-compose up -f ... during local dev (overrides file to expose ports) • Run tests concurrently via Testcontainers without stopping local environment • Seamless transition to CI - use Testcontainers
  • 73. Summary • Generic image container • Specialised Database container • Selenium containers with video recording and VNC debugging • Building a Dockerfile • Docker Compose
  • 75. Speed enhancements • Startup containers in in advance • Checkpoint-Restore In Userspace
  • 76. 'Version 2' • API tidyup • decouple from JUnit 4 and support other frameworks directly
  • 77. Core elements as a library • high-level Docker object API as a library, for more than just testing usage • planning collaboration with Arquillian Cube project team !
  • 78. More things! • Pumba (Chaos testing) - landing soon! • ...
  • 79. Conclusion • Hopefully another useful tool for your testing toolbox • Easy to use, powerful features for many scenarios • Please try it out yourselves!
  • 80. Thanks to • Everyone who has contributed to the project • ZeroTurnaround • You!