testing for unicorns [imworld]

111
Testing For Unicorns From Unit to Deployment Tests Alex Soto @alexsotob

Upload: alex-soto

Post on 22-Jan-2018

149 views

Category:

Software


2 download

TRANSCRIPT

Testing For Unicorns

From Unit to Deployment Tests

Alex Soto@alexsotob

@alexsotob2

Alex Soto

Red Hat Engineer www.lordofthejars.com @alexsotob

Who Am I?

@alexsotob3

https://www.manning.com/books/testing-java-microservices

@alexsotob4

Questions

@alexsotob5

Click to add subtitleASSERTIONS

@alexsotob

JUnit Test

@Test public void should_find_composer_by_name() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart"); // Then: assertEquals("Wolfgang Amadeus Mozart", mozart.getName()); assertEquals(Era.CLASSICAL, mozart.getEra()); assertEquals(LocalDate.of(1756, 1, 27), mozart.getBirthdate()); assertEquals(LocalDate.of(1791, 12, 5), mozart.getDied()); }

6

@alexsotob

JUnit Test

@Test public void should_find_composer_by_name() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart"); // Then: assertEquals("Wolfgang Amadeus Mozart", mozart.getName()); assertEquals(Era.CLASSICAL, mozart.getEra()); assertEquals(LocalDate.of(1756, 1, 27), mozart.getBirthdate()); assertEquals(LocalDate.of(1791, 12, 5), mozart.getDied()); }

6

Readable name

@alexsotob

JUnit Test

@Test public void should_find_composer_by_name() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart"); // Then: assertEquals("Wolfgang Amadeus Mozart", mozart.getName()); assertEquals(Era.CLASSICAL, mozart.getEra()); assertEquals(LocalDate.of(1756, 1, 27), mozart.getBirthdate()); assertEquals(LocalDate.of(1791, 12, 5), mozart.getDied()); }

6

Readable name

BDD style

@alexsotob

JUnit Test

@Test public void should_find_composer_by_name() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart"); // Then: assertEquals("Wolfgang Amadeus Mozart", mozart.getName()); assertEquals(Era.CLASSICAL, mozart.getEra()); assertEquals(LocalDate.of(1756, 1, 27), mozart.getBirthdate()); assertEquals(LocalDate.of(1791, 12, 5), mozart.getDied()); }

6

Readable name

BDD style

AssertEquals Order

@alexsotob

JUnit Test

@Test public void should_find_composer_by_name() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart"); // Then: assertEquals("Wolfgang Amadeus Mozart", mozart.getName()); assertEquals(Era.CLASSICAL, mozart.getEra()); assertEquals(LocalDate.of(1756, 1, 27), mozart.getBirthdate()); assertEquals(LocalDate.of(1791, 12, 5), mozart.getDied()); }

6

Readable name

BDD style

AssertEquals Order

Depends On Equals

@alexsotob7

AssertJ

http://joel-costigliola.github.io/assertj/

@alexsotob

AssertJ Testimport static org.assertj.core.api.Assertions.assertThat;

@Test public void should_find_composer_by_name() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart”);

// Then: assertThat(mozart.getName()).isEqualTo("Wolfgang Amadeus Mozart”); assertThat(mozart).returns("Wolfgang Amadeus Mozart", Composer::getName); assertThat(mozart.getBirthdate()).isEqualTo(LocalDate.of(1756, 1, 27));

assertThat(mozart).isEqualToComparingFieldByField(expectedMozart); assertThat(mozart).isEqualToIgnoringNullFields(expectedMozart); }

8

@alexsotob

AssertJ Testimport static org.assertj.core.api.Assertions.assertThat;

@Test public void should_find_composer_by_name() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart”);

// Then: assertThat(mozart.getName()).isEqualTo("Wolfgang Amadeus Mozart”); assertThat(mozart).returns("Wolfgang Amadeus Mozart", Composer::getName); assertThat(mozart.getBirthdate()).isEqualTo(LocalDate.of(1756, 1, 27));

assertThat(mozart).isEqualToComparingFieldByField(expectedMozart); assertThat(mozart).isEqualToIgnoringNullFields(expectedMozart); }

8

Static Import

@alexsotob

AssertJ Testimport static org.assertj.core.api.Assertions.assertThat;

@Test public void should_find_composer_by_name() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart”);

// Then: assertThat(mozart.getName()).isEqualTo("Wolfgang Amadeus Mozart”); assertThat(mozart).returns("Wolfgang Amadeus Mozart", Composer::getName); assertThat(mozart.getBirthdate()).isEqualTo(LocalDate.of(1756, 1, 27));

assertThat(mozart).isEqualToComparingFieldByField(expectedMozart); assertThat(mozart).isEqualToIgnoringNullFields(expectedMozart); }

8

Static Import

Readable Assertions

@alexsotob

AssertJ Testimport static org.assertj.core.api.Assertions.assertThat;

@Test public void should_find_composer_by_name() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart”);

// Then: assertThat(mozart.getName()).isEqualTo("Wolfgang Amadeus Mozart”); assertThat(mozart).returns("Wolfgang Amadeus Mozart", Composer::getName); assertThat(mozart.getBirthdate()).isEqualTo(LocalDate.of(1756, 1, 27));

assertThat(mozart).isEqualToComparingFieldByField(expectedMozart); assertThat(mozart).isEqualToIgnoringNullFields(expectedMozart); }

8

Static Import

Readable Assertions

Not Depending on Equals

@alexsotob

AssertJ Test Collections

@Test public void should_find_operas_by_composer_name() {

// Given: Composers composers = new Composers();

// When: final List<Opera> operas = composers.findOperasByComposerName("Wolfgang Amadeus Mozart");

// Then: assertThat(operas) .hasSize(2) .extracting(Opera::getName) .containsExactlyInAnyOrder("Die Zauberflöte", "Don Giovanni”);

}

9

@alexsotob

AssertJ Test Collections

@Test public void should_find_operas_by_composer_name() {

// Given: Composers composers = new Composers();

// When: final List<Opera> operas = composers.findOperasByComposerName("Wolfgang Amadeus Mozart");

// Then: assertThat(operas) .hasSize(2) .extracting(Opera::getName) .containsExactlyInAnyOrder("Die Zauberflöte", "Don Giovanni”);

}

9

Train Call (IDE support)

@alexsotob

AssertJ Test Collections

@Test public void should_find_operas_by_composer_name() {

// Given: Composers composers = new Composers();

// When: final List<Opera> operas = composers.findOperasByComposerName("Wolfgang Amadeus Mozart");

// Then: assertThat(operas) .hasSize(2) .extracting(Opera::getName) .containsExactlyInAnyOrder("Die Zauberflöte", "Don Giovanni”);

}

9

Train Call (IDE support) Create List with

getName Result

@alexsotob

AssertJ Test Collections

@Test public void should_find_operas_by_composer_name() {

// Given: Composers composers = new Composers();

// When: final List<Opera> operas = composers.findOperasByComposerName("Wolfgang Amadeus Mozart");

// Then: assertThat(operas) .hasSize(2) .extracting(Opera::getName) .containsExactlyInAnyOrder("Die Zauberflöte", "Don Giovanni”);

}

9

Train Call (IDE support) Create List with

getName Result

Methods for String

@alexsotob

AssertJ Soft Assertions@Test public void should_find_composer_by_name_soft_assertions() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart");

// Then: SoftAssertions.assertSoftly(softly -> { softly.assertThat(mozart.getName()).isEqualTo("Wolfgang Amadeus Mozart"); softly.assertThat(mozart.getEra()).isEqualTo(Era.CLASSICAL); softly.assertThat(mozart.getBirthdate()).isEqualTo(LocalDate.of(1756, 1, 27)); softly.assertThat(mozart.getDied()).isEqualTo(LocalDate.of(1791, 12, 5)); });

}

10

@alexsotob

AssertJ Soft Assertions@Test public void should_find_composer_by_name_soft_assertions() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart");

// Then: SoftAssertions.assertSoftly(softly -> { softly.assertThat(mozart.getName()).isEqualTo("Wolfgang Amadeus Mozart"); softly.assertThat(mozart.getEra()).isEqualTo(Era.CLASSICAL); softly.assertThat(mozart.getBirthdate()).isEqualTo(LocalDate.of(1756, 1, 27)); softly.assertThat(mozart.getDied()).isEqualTo(LocalDate.of(1791, 12, 5)); });

}

10

Java 8 Lambda

@alexsotob

AssertJ Soft Assertions@Test public void should_find_composer_by_name_soft_assertions() {

// Given: Composers composers = new Composers();

// When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart");

// Then: SoftAssertions.assertSoftly(softly -> { softly.assertThat(mozart.getName()).isEqualTo("Wolfgang Amadeus Mozart"); softly.assertThat(mozart.getEra()).isEqualTo(Era.CLASSICAL); softly.assertThat(mozart.getBirthdate()).isEqualTo(LocalDate.of(1756, 1, 27)); softly.assertThat(mozart.getDied()).isEqualTo(LocalDate.of(1791, 12, 5)); });

}

10

Java 8 Lambda

All assertions in Block

@alexsotob11

Don’t Sleep, Just Wait

@alexsotob

Asynchronous Call

@Test public void should_play_operas() throws InterruptedException {

// Given: final Opera nozzeDiFigaro = ...; Gramophone gramophone = new Gramophone();

// When: gramophone.play(nozzeDiFigaro);

// Then: TimeUnit.SECONDS.sleep(3);

assertThat(gramophone.getCurrentOpera()).isEqualTo(nozzeDiFigaro);

}

12

@alexsotob

Asynchronous Call

@Test public void should_play_operas() throws InterruptedException {

// Given: final Opera nozzeDiFigaro = ...; Gramophone gramophone = new Gramophone();

// When: gramophone.play(nozzeDiFigaro);

// Then: TimeUnit.SECONDS.sleep(3);

assertThat(gramophone.getCurrentOpera()).isEqualTo(nozzeDiFigaro);

}

12

Asynchronous Call

@alexsotob

Asynchronous Call

@Test public void should_play_operas() throws InterruptedException {

// Given: final Opera nozzeDiFigaro = ...; Gramophone gramophone = new Gramophone();

// When: gramophone.play(nozzeDiFigaro);

// Then: TimeUnit.SECONDS.sleep(3);

assertThat(gramophone.getCurrentOpera()).isEqualTo(nozzeDiFigaro);

}

12

Asynchronous Call

Slowest Machine Time

@alexsotob13

https://github.com/awaitility/awaitility

@alexsotob

Awaitility Example@Test public void should_play_operas_version_2() {

// Given: final Opera nozzeDiFigaro = Composers.OperaFactory .createOpera("Le Nozze di Figaro") .language(Language.ITALIAN).librettist("Lorenzo Da Ponte") .roles("Count Almaviva", "Countess Rosina", "Susanna", "Figaro") .build();

Gramophone gramophone = new Gramophone();

// When: gramophone.play(nozzeDiFigaro);

// Then: await().atMost(5, TimeUnit.SECONDS).until(gramophone::isPlaying); assertThat(gramophone.getCurrentOpera()).isEqualTo(nozzeDiFigaro);

}

14

@alexsotob

Awaitility Example@Test public void should_play_operas_version_2() {

// Given: final Opera nozzeDiFigaro = Composers.OperaFactory .createOpera("Le Nozze di Figaro") .language(Language.ITALIAN).librettist("Lorenzo Da Ponte") .roles("Count Almaviva", "Countess Rosina", "Susanna", "Figaro") .build();

Gramophone gramophone = new Gramophone();

// When: gramophone.play(nozzeDiFigaro);

// Then: await().atMost(5, TimeUnit.SECONDS).until(gramophone::isPlaying); assertThat(gramophone.getCurrentOpera()).isEqualTo(nozzeDiFigaro);

}

14

Asynchronous Call

@alexsotob

Awaitility Example@Test public void should_play_operas_version_2() {

// Given: final Opera nozzeDiFigaro = Composers.OperaFactory .createOpera("Le Nozze di Figaro") .language(Language.ITALIAN).librettist("Lorenzo Da Ponte") .roles("Count Almaviva", "Countess Rosina", "Susanna", "Figaro") .build();

Gramophone gramophone = new Gramophone();

// When: gramophone.play(nozzeDiFigaro);

// Then: await().atMost(5, TimeUnit.SECONDS).until(gramophone::isPlaying); assertThat(gramophone.getCurrentOpera()).isEqualTo(nozzeDiFigaro);

}

14

Asynchronous Call

Polls until True or Timeout

@alexsotob

Deadlock Detection

Different PollingsFixed, Fibonacci, Iterative, Custom

Simple LibraryNo dependencies, No magic

Benefits of Awaitility

15

@alexsotob16

REST API

@alexsotob

GET /Ludwig+van+Beethoven

{

"name": "Ludwig van Beethoven", "era": "ROMANTIC", "birthdate": {}, "died": {}, "operas": [ { "name": "Fidelio", "librettist": "Georg Friedrich Treitschke", "language": "GERMAN", "roles": ["Rocco", "Leonore", "Florestan"] } ] }

17

@alexsotob

GET /Ludwig+van+Beethoven

{

"name": "Ludwig van Beethoven", "era": "ROMANTIC", "birthdate": {}, "died": {}, "operas": [ { "name": "Fidelio", "librettist": "Georg Friedrich Treitschke", "language": "GERMAN", "roles": ["Rocco", "Leonore", "Florestan"] } ] }

17

Simple Objects

@alexsotob

GET /Ludwig+van+Beethoven

{

"name": "Ludwig van Beethoven", "era": "ROMANTIC", "birthdate": {}, "died": {}, "operas": [ { "name": "Fidelio", "librettist": "Georg Friedrich Treitschke", "language": "GERMAN", "roles": ["Rocco", "Leonore", "Florestan"] } ] }

17

Simple Objects

Array of Objects

@alexsotob

HttpClient Example

@Test public void should_find_composer() throws IOException, URISyntaxException {

// Given: URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/"); uriBuilder.setPath("Ludwig van Beethoven");

// When: final Content bodyContent = Request.Get(uriBuilder.build()) .execute().returnContent();

String body = bodyContent.asString();

// Then: assertThat(body).contains("\"name\":\"Ludwig van Beethoven\"") .contains("\"librettist\":\"Georg Friedrich Treitschke\""); }

18

@alexsotob

HttpClient Example

@Test public void should_find_composer() throws IOException, URISyntaxException {

// Given: URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/"); uriBuilder.setPath("Ludwig van Beethoven");

// When: final Content bodyContent = Request.Get(uriBuilder.build()) .execute().returnContent();

String body = bodyContent.asString();

// Then: assertThat(body).contains("\"name\":\"Ludwig van Beethoven\"") .contains("\"librettist\":\"Georg Friedrich Treitschke\""); }

18

Prepare Connection

@alexsotob

HttpClient Example

@Test public void should_find_composer() throws IOException, URISyntaxException {

// Given: URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/"); uriBuilder.setPath("Ludwig van Beethoven");

// When: final Content bodyContent = Request.Get(uriBuilder.build()) .execute().returnContent();

String body = bodyContent.asString();

// Then: assertThat(body).contains("\"name\":\"Ludwig van Beethoven\"") .contains("\"librettist\":\"Georg Friedrich Treitschke\""); }

18

Prepare Connection

Do connection

@alexsotob

HttpClient Example

@Test public void should_find_composer() throws IOException, URISyntaxException {

// Given: URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/"); uriBuilder.setPath("Ludwig van Beethoven");

// When: final Content bodyContent = Request.Get(uriBuilder.build()) .execute().returnContent();

String body = bodyContent.asString();

// Then: assertThat(body).contains("\"name\":\"Ludwig van Beethoven\"") .contains("\"librettist\":\"Georg Friedrich Treitschke\""); }

18

Prepare Connection

Do connection

Get content

@alexsotob

HttpClient Example

@Test public void should_find_composer() throws IOException, URISyntaxException {

// Given: URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/"); uriBuilder.setPath("Ludwig van Beethoven");

// When: final Content bodyContent = Request.Get(uriBuilder.build()) .execute().returnContent();

String body = bodyContent.asString();

// Then: assertThat(body).contains("\"name\":\"Ludwig van Beethoven\"") .contains("\"librettist\":\"Georg Friedrich Treitschke\""); }

18

Prepare Connection

Do connection

Get content

Manipulate String

@alexsotob19

http://rest-assured.io/

@alexsotob

REST-assured Example

@Test public void should_find_composer() {

given() .when() .get("{composer}", "Ludwig van Beethoven") .then() .assertThat() .body("name", is("Ludwig van Beethoven")) .body("operas.size()", is(1)) .body("operas.name", hasItems("Fidelio"));

}

20

@alexsotob

REST-assured Example

@Test public void should_find_composer() {

given() .when() .get("{composer}", "Ludwig van Beethoven") .then() .assertThat() .body("name", is("Ludwig van Beethoven")) .body("operas.size()", is(1)) .body("operas.name", hasItems("Fidelio"));

}

20

GET Http Method with Placeholders

@alexsotob

REST-assured Example

@Test public void should_find_composer() {

given() .when() .get("{composer}", "Ludwig van Beethoven") .then() .assertThat() .body("name", is("Ludwig van Beethoven")) .body("operas.size()", is(1)) .body("operas.name", hasItems("Fidelio"));

}

20

GET Http Method with Placeholders

GPath Expression

@alexsotob

REST-assured Request Specification

.get("http://example.com/{composer}", "Ludwig van Beethoven")

RequestSpecBuilder builder = new RequestSpecBuilder(); builder.setBaseUri("http://example.com");

given().spec(builder.build())...

21

@alexsotob

REST-assured Request Specification

.get("http://example.com/{composer}", "Ludwig van Beethoven")

RequestSpecBuilder builder = new RequestSpecBuilder(); builder.setBaseUri("http://example.com");

given().spec(builder.build())...

21

Use domain directly

@alexsotob

REST-assured Request Specification

.get("http://example.com/{composer}", "Ludwig van Beethoven")

RequestSpecBuilder builder = new RequestSpecBuilder(); builder.setBaseUri("http://example.com");

given().spec(builder.build())...

21

Use domain directly

Use Spec Builder

@alexsotob

REST-assured Request Specification

.get("http://example.com/{composer}", "Ludwig van Beethoven")

RequestSpecBuilder builder = new RequestSpecBuilder(); builder.setBaseUri("http://example.com");

given().spec(builder.build())...

21

Use domain directly

Use Spec Builder

Reuse Everywhere

@alexsotob

REST-assured Auth

given().auth().oauth2(accessToken).when()...

given().auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf")).when()...

given().auth().basic("username", "password").when()...

22

@alexsotob

Custom ParsersNot just JSON and XML

SSL Support.relaxedHTTPSValidation()

FiltersInput/Output modification

JSON Schema ValidationContent not Important

More Featuresof Rest-Assured

23

@alexsotob24

(Micro) Services Dependencies

@alexsotob

Network Down

Service Down

Limits on API

Component Not in Control

Micro-Services Dependencies Hell

Problems with Services

25

@alexsotob

Mock Http Component

Stub/Fake Http Server

Service Virtualization

Possible Solutions

26

@alexsotob27

Service Virtualization

@alexsotob27

Service Virtualization

@alexsotob

Service Virtualization Capture Mode

28

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Capture Mode

28

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Capture Mode

28

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Capture Mode

28

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Capture Mode

28

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Capture Mode

28

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Simulate Mode

29

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Simulate Mode

29

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Simulate Mode

29

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Simulate Mode

29

Service A External Network Service B

Scripts

Proxy

@alexsotob

Service Virtualization Simulate Mode

29

Service A External Network Service B

Scripts

Proxy

@alexsotob30

https://hoverfly.io/

@alexsotob

Hoverfly Example

@ClassRule public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureOrSimulationMode("getcomposers.json");

@Test public void should_get_composers_from_composers_microservice() {

// Given: ComposersGateway composersGateway = new ComposersGateway("operas.com", 8081);

// When: Composer composer = composersGateway.getComposer("Ludwig van Beethoven");

// Then: assertThat(composer.getName()).isEqualTo("Ludwig van Beethoven");

}

31

@alexsotob

Hoverfly Example

@ClassRule public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureOrSimulationMode("getcomposers.json");

@Test public void should_get_composers_from_composers_microservice() {

// Given: ComposersGateway composersGateway = new ComposersGateway("operas.com", 8081);

// When: Composer composer = composersGateway.getComposer("Ludwig van Beethoven");

// Then: assertThat(composer.getName()).isEqualTo("Ludwig van Beethoven");

}

31

Start Hoverfly

@alexsotob

Hoverfly Example

@ClassRule public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureOrSimulationMode("getcomposers.json");

@Test public void should_get_composers_from_composers_microservice() {

// Given: ComposersGateway composersGateway = new ComposersGateway("operas.com", 8081);

// When: Composer composer = composersGateway.getComposer("Ludwig van Beethoven");

// Then: assertThat(composer.getName()).isEqualTo("Ludwig van Beethoven");

}

31

Start Hoverfly

Use Real Host

@alexsotob

Hoverfly Example

@ClassRule public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureOrSimulationMode("getcomposers.json");

@Test public void should_get_composers_from_composers_microservice() {

// Given: ComposersGateway composersGateway = new ComposersGateway("operas.com", 8081);

// When: Composer composer = composersGateway.getComposer("Ludwig van Beethoven");

// Then: assertThat(composer.getName()).isEqualTo("Ludwig van Beethoven");

}

31

Start Hoverfly

Use Real Host

Get Data from Real

@alexsotob

Hoverfly Example

@ClassRule public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureOrSimulationMode("getcomposers.json");

@Test public void should_get_composers_from_composers_microservice() {

// Given: ComposersGateway composersGateway = new ComposersGateway("operas.com", 8081);

// When: Composer composer = composersGateway.getComposer("Ludwig van Beethoven");

// Then: assertThat(composer.getName()).isEqualTo("Ludwig van Beethoven");

}

31

Start Hoverfly

Use Real Host

Get Data from Real

{ "data" : { "pairs" : [{ "request" : { "path" : "/Ludwig van Beethoven", "method" : "GET", "destination" : “operas.com:8081", ... } "response" : { "status" : 200, "body" : "{\"name\":\"Ludwig van Beethoven\",}", "encodedBody" : false, "headers" : { "Connection" : [ "keep-alive" ], ... } } } }

@alexsotob

Hoverfly Example

@ClassRule public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureOrSimulationMode("getcomposers.json");

@Test public void should_get_composers_from_composers_microservice() {

// Given: ComposersGateway composersGateway = new ComposersGateway("operas.com", 8081);

// When: Composer composer = composersGateway.getComposer("Ludwig van Beethoven");

// Then: assertThat(composer.getName()).isEqualTo("Ludwig van Beethoven");

}

31

Start Hoverfly

Use Real Host

Get Data from Real

@alexsotob

Hoverfly Example

@ClassRule public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureOrSimulationMode("getcomposers.json");

@Test public void should_get_composers_from_composers_microservice() {

// Given: ComposersGateway composersGateway = new ComposersGateway("operas.com", 8081);

// When: Composer composer = composersGateway.getComposer("Ludwig van Beethoven");

// Then: assertThat(composer.getName()).isEqualTo("Ludwig van Beethoven");

}

31

Start Hoverfly

Use Real Host

Get Data from Proxy

@alexsotob32

Containers Are Burning

@alexsotob32

Containers Are Burning

@alexsotob

Testing Containers

docker build -t myorg/myservice:1.0.0 . docker run --rm -ti -p 8080:8080 myorg/myservice:1.0.0

docker-compose up

mvn clean test

docker-compose stop

33

@alexsotob

Testing Containers

docker build -t myorg/myservice:1.0.0 . docker run --rm -ti -p 8080:8080 myorg/myservice:1.0.0

docker-compose up

mvn clean test

docker-compose stop

33

Docker Run

@alexsotob

Testing Containers

docker build -t myorg/myservice:1.0.0 . docker run --rm -ti -p 8080:8080 myorg/myservice:1.0.0

docker-compose up

mvn clean test

docker-compose stop

33

Docker Run

Docker Compose Run

@alexsotob

Testing Containers

docker build -t myorg/myservice:1.0.0 . docker run --rm -ti -p 8080:8080 myorg/myservice:1.0.0

docker-compose up

mvn clean test

docker-compose stop

33

Docker Run

Docker Compose Run

Run tests

@alexsotob

Testing Containers

docker build -t myorg/myservice:1.0.0 . docker run --rm -ti -p 8080:8080 myorg/myservice:1.0.0

docker-compose up

mvn clean test

docker-compose stop

33

Docker Run

Docker Compose Run

Run tests

Stop Docker Containers

@alexsotob34

http://arquillian.org/arquillian-cube/

@alexsotob

Arquillian Cube Example@RunWith(Arquillian.class) public class HelloWorldTest {

@ArquillianResource @DockerUrl(containerName = "helloworld", exposedPort = 8080) RequestSpecBuilder requestSpecBuilder;

@Test public void should_receive_ok_message() { RestAssured .given() .spec(requestSpecBuilder.build()) .when() .get() .then() .assertThat().body("status", equalTo("OK")); }

}

35

@alexsotob

Arquillian Cube Example@RunWith(Arquillian.class) public class HelloWorldTest {

@ArquillianResource @DockerUrl(containerName = "helloworld", exposedPort = 8080) RequestSpecBuilder requestSpecBuilder;

@Test public void should_receive_ok_message() { RestAssured .given() .spec(requestSpecBuilder.build()) .when() .get() .then() .assertThat().body("status", equalTo("OK")); }

}

35

Arquillian Runner

@alexsotob

Arquillian Cube Example@RunWith(Arquillian.class) public class HelloWorldTest {

@ArquillianResource @DockerUrl(containerName = "helloworld", exposedPort = 8080) RequestSpecBuilder requestSpecBuilder;

@Test public void should_receive_ok_message() { RestAssured .given() .spec(requestSpecBuilder.build()) .when() .get() .then() .assertThat().body("status", equalTo("OK")); }

}

35

Arquillian Runner

REST-Assured Integration Environment Resolver

@alexsotob

Arquillian Cube Example@RunWith(Arquillian.class) public class HelloWorldTest {

@ArquillianResource @DockerUrl(containerName = "helloworld", exposedPort = 8080) RequestSpecBuilder requestSpecBuilder;

@Test public void should_receive_ok_message() { RestAssured .given() .spec(requestSpecBuilder.build()) .when() .get() .then() .assertThat().body("status", equalTo("OK")); }

}

35

Arquillian Runner

REST-Assured Integration Environment Resolver

Normal REST-Assured Call

@alexsotob

Arquillian Cube Example@RunWith(Arquillian.class) public class HelloWorldTest {

@ArquillianResource @DockerUrl(containerName = "helloworld", exposedPort = 8080) RequestSpecBuilder requestSpecBuilder;

@Test public void should_receive_ok_message() { RestAssured .given() .spec(requestSpecBuilder.build()) .when() .get() .then() .assertThat().body("status", equalTo("OK")); }

}

35

Arquillian Runner

REST-Assured Integration Environment Resolver

Normal REST-Assured Call

helloworld: image: jonmorehouse/ping-pong ports: - "8080:8080"

src/test/docker/docker-compose.yml

@alexsotob

Arquillian Cube DSL

@RunWith(SpringRunner.class) @SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT) @ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class) public class PingPongSpringBootTest {

@ClassRule public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6") .withPortBinding(6379); @Autowired TestRestTemplate restTemplate;

@Test public void should_get_data_from_redis() { }

36

@alexsotob

Arquillian Cube DSL

@RunWith(SpringRunner.class) @SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT) @ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class) public class PingPongSpringBootTest {

@ClassRule public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6") .withPortBinding(6379); @Autowired TestRestTemplate restTemplate;

@Test public void should_get_data_from_redis() { }

36

Spring Boot Test

@alexsotob

Arquillian Cube DSL

@RunWith(SpringRunner.class) @SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT) @ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class) public class PingPongSpringBootTest {

@ClassRule public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6") .withPortBinding(6379); @Autowired TestRestTemplate restTemplate;

@Test public void should_get_data_from_redis() { }

36

Spring Boot Test

Custom Initializer

@alexsotob

Arquillian Cube DSL

@RunWith(SpringRunner.class) @SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT) @ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class) public class PingPongSpringBootTest {

@ClassRule public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6") .withPortBinding(6379); @Autowired TestRestTemplate restTemplate;

@Test public void should_get_data_from_redis() { }

36

Spring Boot Test

Custom Initializer

Container Definition

@alexsotob

Arquillian Cube DSL

@RunWith(SpringRunner.class) @SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT) @ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class) public class PingPongSpringBootTest {

@ClassRule public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6") .withPortBinding(6379); @Autowired TestRestTemplate restTemplate;

@Test public void should_get_data_from_redis() { }

36

Spring Boot Test

Custom Initializer

Container Definitionpublic static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

@Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) {

EnvironmentTestUtils.addEnvironment("testcontainers", configurableApplicationContext.getEnvironment(), "spring.redis.host=" + redis.getIpAddress(), "spring.redis.port=" + redis.getBindPort(6379) );

@alexsotob

Arquillian Cube DSL

@RunWith(SpringRunner.class) @SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT) @ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class) public class PingPongSpringBootTest {

@ClassRule public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6") .withPortBinding(6379); @Autowired TestRestTemplate restTemplate;

@Test public void should_get_data_from_redis() { }

36

Spring Boot Test

Custom Initializer

Container Definitionpublic static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

@Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) {

EnvironmentTestUtils.addEnvironment("testcontainers", configurableApplicationContext.getEnvironment(), "spring.redis.host=" + redis.getIpAddress(), "spring.redis.port=" + redis.getBindPort(6379) );

Sets Container Environment

@alexsotob

Arquillian Cube K8S/OpenShift

@RunWith(Arquillian.class) public class HelloWorldTest {

@ArquillianResource KubernetesClient client;

@Named("hello-world-service") @PortForward @ArquillianResource URL url;

@Test public void testRunningPodStaysUp() throws Exception { assertThat(client).deployments().pods().isPodReadyForPeriod(); }

}

37

@alexsotob

Arquillian Cube K8S/OpenShift

@RunWith(Arquillian.class) public class HelloWorldTest {

@ArquillianResource KubernetesClient client;

@Named("hello-world-service") @PortForward @ArquillianResource URL url;

@Test public void testRunningPodStaysUp() throws Exception { assertThat(client).deployments().pods().isPodReadyForPeriod(); }

}

37

Kubernetes Client

@alexsotob

Arquillian Cube K8S/OpenShift

@RunWith(Arquillian.class) public class HelloWorldTest {

@ArquillianResource KubernetesClient client;

@Named("hello-world-service") @PortForward @ArquillianResource URL url;

@Test public void testRunningPodStaysUp() throws Exception { assertThat(client).deployments().pods().isPodReadyForPeriod(); }

}

37

Kubernetes Client

URL to Access Service

@alexsotob

Arquillian Cube K8S/OpenShift

@RunWith(Arquillian.class) public class HelloWorldTest {

@ArquillianResource KubernetesClient client;

@Named("hello-world-service") @PortForward @ArquillianResource URL url;

@Test public void testRunningPodStaysUp() throws Exception { assertThat(client).deployments().pods().isPodReadyForPeriod(); }

}

37

Kubernetes Client

URL to Access Service

AssertJ Custom Assertions

@alexsotob38

Smart Testing

http://arquillian.org/smart-testing/

@alexsotob39

Production Sources Tests

https://martinfowler.com/articles/rise-test-impact-analysis.html

@alexsotob39

Production Sources Tests

https://martinfowler.com/articles/rise-test-impact-analysis.html

@alexsotob39

Production Sources Tests

https://martinfowler.com/articles/rise-test-impact-analysis.html

@alexsotob

Smart Testing Maven Extension

curl -sSL https://git.io/v5jy6 | bash

40

@alexsotob

Smart Testing Maven Extension

curl -sSL https://git.io/v5jy6 | bash

40

Installs extension

@alexsotob

Smart Testing Maven Extension

curl -sSL https://git.io/v5jy6 | bash

40

Installs extension

mvn clean test -Dsmart.testing="new, changed, affected"

@alexsotob

Smart Testing Maven Extension

curl -sSL https://git.io/v5jy6 | bash

40

Installs extension

mvn clean test -Dsmart.testing="new, changed, affected"

Runs ONLY new, changed and prod related tests

@alexsotob

Smart Testing Maven Extension

curl -sSL https://git.io/v5jy6 | bash

40

Installs extension

mvn clean test -Dsmart.testing="new, changed, affected"

Runs ONLY new, changed and prod related tests

mvn clean test -Dsmart.testing.mode=ordering -Dsmart.testing="new, changed, affected”

@alexsotob

Smart Testing Maven Extension

curl -sSL https://git.io/v5jy6 | bash

40

Installs extension

mvn clean test -Dsmart.testing="new, changed, affected"

Runs ONLY new, changed and prod related tests

mvn clean test -Dsmart.testing.mode=ordering -Dsmart.testing="new, changed, affected”

Prioritize new, changed and prod related tests

@alexsotob41

Let’s Wind Down

@alexsotob

From Low to High LevelUnit, Component, Integration, Deployment

Not Just for Micro-ServicesSame can be reused for monolithic

Improves ReadabilityTests are meant to be read

Conclusions

42

Deployment VelocityFrom week to several times per day

@alexsotob43

Keep CalmAnd Write Tests

http://bit.ly/2xT2syU