KEMBAR78
Reproducible component tests using docker | PPTX
Reproducible component tests for
microservices using Docker
Pavel Rabetski
Component tests
● Verify behaviour of a component
● Cheaper to maintain than system tests
● Easier to locate problems compared to system tests
Monolithic app vs Micro Services
Monolithic app
MS
1
MS
3
MS
2
MS
4
● same lifecycle
● same runtime/environment
● component is a “grey box”
● different lifecycle
● different runtime/environment
● component is a “black box”
Component tests
Test
script
MS
Test env
Component tests
S0 S1T1:
S0 S2T2:
S0 S3T3:
Test script
Component tests: tests in sequence
S0 S1T1:
S1 ?T2:
Component tests: repeat same test
S0 S3T3:
S3 ?T3:
Approach 1: cleanup/reset state
S0 S1T1:
S1 S0T1 cleanup:
S0 S2T2:
S2 S0T2 cleanup:
Extra development effort:
keep track of changes and add
cleanup code
Not always possible: e.g.
deleting an entity just marks
delete property = true
Approach 2: separate contexts
Sx SnTn:
UserId=n
S0 S1T1:
UserId=1
Extra development effort:
keep track of the unique
context
Not always possible: if we
have a global resource or
complex pre-generated data
S1 S2T2:
UserId=2
Alternative approach: recreate test environment
Test
script
Less development effort: no
need to separate contexts or
clean up after test
No constraints from previous
methods: possible with global
resource or pre-generated data
MS
Test env
MS
Test env
MS
Test env
Alternative approach: recreate test environment
Monolithic app
Alternative approach: recreate test environment
Monolithic app
Alternative approach: recreate test environment
Monolithic app
MS
pavradev/dockerbay
Apache 2.0
It is Java library for component testing
It does orchestrate docker environment
It uses JUnit and Docker Java Client
Example: Hotel booking web app
webapp
db
service webapp
db service
Maven dependency
<dependency>
<groupId>com.github.pavradev</groupId>
<artifactId>dockerbay</artifactId>
<version>${dockerbay.version}</version>
<scope>test</scope>
</dependency>
Configuring dockerbay: JUnit Rule
@Rule
public DockerRule dockerRule = DockerRule.getDefault()
.addContainer(postgresContainer)
.addContainer(reviewsMockContainer)
.addContainer(webappContainer);
Configuring dockerbay: db container
private ContainerConfig postgresContainer = ContainerConfig.builder()
.withImage("postgres:9")
.withName("postgres")
.waitForLogEntry("database system is ready to accept connections")
.build();
Configuring dockerbay: webapp container
private ContainerConfig webapp = ContainerConfig.builder()
.withImage("demo/webapp")
.withName("webapp")
.waitForUrl("/api/hotels")
.withExposedTcpPort(8082)
.build();
Configuring dockerbay: service container
private ContainerConfig reviewsMockContainer = ContainerConfig.builder()
.withImage("ekino/wiremock:2.1.11")
.withName("reviews-mock")
.withExposedTcpPort(8083)
.withCmd(Arrays.asList("--port", "8083", "--verbose"))
.build();
Add tests
public class WebappCT {
@Test
public void shouldGetHotelWithReviews() {
...
}
@Test
public void shouldUpdateHotel() {
...
}
@Test
public void shouldDeleteHotel() {
...
}
}
Add tests
@Test
public void shouldUpdateHotel() {
//create dummy hotel
reqSpec.contentType(ContentType.JSON)
.body("{"name":"Dummy hotel","address":"Dummy address","zip":"4001"}")
.post("/api/hotels");
//update dummy hotel address
reqSpec.contentType(ContentType.JSON)
.body("{"address":"Updated address","zip":"4004"}")
.put("/api/hotels/1");
//validate address updated
reqSpec.get("/api/hotels/1")
.then()
.assertThat()
.body("address", equalTo("Updated address"));
}
Add tests
@Test
public void shouldGetHotelWithReviews() {
//create dummy hotel
reqSpec.contentType(ContentType.JSON)
.body("{"name":"Dummy hotel","address":"Dummy address","zip":"4001"}")
.post("/api/hotels");
//mock GET request for hotel reviews
reviewsMock.register(get(urlEqualTo("/api/reviews?hotelId=1"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("[...]")));
//validate dummy hotel returned with reviews
reqSpec.get("/api/hotels/1")
.then().assertThat()
.statusCode(200)
.and().body("name", equalTo("Dummy hotel"))
.and().body("reviews.size()", equalTo(1));
}
Execute
dockerbay
DOCKER_HOST
Execute
pull images
DOCKER_HOST
WM
Execute
pull images
create custom network
DOCKER_HOST
WM
{id}
Execute
pull images
create custom network
create and start containers
DOCKER_HOST
WM
{id}
webapp
db service
{id}-webapp {id}-db {id}-service
Execute
pull images
create custom network
create and start containers
execute test
DOCKER_HOST
WM
{id}
webapp
db service
{id}-webapp {id}-db {id}-service
Execute
pull images
create custom network
create and start containers
stop and remove containers
execute test
DOCKER_HOST
WM
{id}
Execute
pull images
create custom network
create and start containers
stop and remove containers
execute test
remove custom network
DOCKER_HOST
WM
Parallelization
T1
T2
T3
Test script
Parallelization
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<parallel>all</parallel>
<threadCount>3</threadCount>
<perCoreThreadCount>false</perCoreThreadCount>
</configuration>
</plugin>
Questions

Reproducible component tests using docker

Editor's Notes

  • #5 MS=web app + database Test env = file system, queues, mocks
  • #9 Usually happens through some interface (like REST). But “inverse function” not always exists. Alternative cleanup (not programmatically but with a db script or smth). It is technology specific solution.
  • #10 Example of unique context tracking is World object in Cucumber framework
  • #12 Slow and complex
  • #13 Slow and complex
  • #14 Mention docker: show info what it is
  • #17 Transitive dependencies to JUnit and Java Docker Client Docker Remote API v1.22 (Docker version 1.10.x) https://docs.docker.com/engine/userguide/networking/work-with-networks/#linking-containers-in-user-defined-networks Linking containers in user-defined networks Docker Client Mac compatiblity: https://github.com/docker-java/docker-java/issues/537
  • #28 docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Names}}"