bdd-driven microservices

Post on 28-Jul-2015

1.117 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Tools, techniques and reflections

BDD-Driven Microservices in Java

John Ferguson

Smart

Introductions“I help teams of smart people

learn to work together more efficiently, to deliver better software faster”

Independently Deployable

Bounded ContextsLoosely CoupledSingle Responsibility

Microservices 101

Independently Deployable

Bounded ContextsLoosely CoupledSingle Responsibility

Microservices 101

Independently Deployable

Bounded ContextsLoosely CoupledSingle Responsibility

Microservices 101

Independently Deployable

Bounded ContextsLoosely CoupledSingle Responsibility

Microservices 101

Independently Deployable

Bounded ContextsLoosely CoupledSingle Responsibility

Microservices 101

Independently Deployable

Bounded ContextsLoosely CoupledSingle Responsibility

Microservices 101

Microservices 101

Movie Catalog Service

Authentication Service

Shopping Cart Service

Delivery Service

Payment Service

REST/HTTP

Microservices 101

Movie Catalog Service

Authentication Service

Shopping Cart Service

Delivery Service

Payment Service

Event Bus

Microservices 101Movie Catalog Service

Microservices 101Movie Catalog Service

Resource

Microservices 101Movie Catalog Service

Resource

Service Layer

Domain model

Repositories

Microservices 101Movie Catalog Service

Resource

Persistance Layer

Service Layer

Domain model

Repositories

Microservices 101Movie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

The testing pyramid

The testing pyramid

Unit Tests

The testing pyramid

Unit Tests

Integration Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Unit Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Unit Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Unit Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Unit Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Unit Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Unit Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Unit Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Unit Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Unit Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Integration Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Integration Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Integration Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Integration Tests

The testing pyramidMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Integration Tests

The testing pyramid End-to-End Tests

Movie Catalog Service

Authentication Service

Shopping Cart Service

Delivery Service

Payment Service

Event Bus

The testing pyramid End-to-End Tests

Movie Catalog Service

Authentication Service

Shopping Cart Service

Delivery Service

Payment Service

Event Bus

The testing pyramid End-to-End Tests

Movie Catalog Service

Authentication Service

Shopping Cart Service

Delivery Service

Payment Service

Event Bus

The testing pyramid End-to-End Tests

Movie Catalog Service

Authentication Service

Shopping Cart Service

Delivery Service

Payment Service

Event Bus

The testing pyramid End-to-End Tests

Movie Catalog Service

Authentication Service

Shopping Cart Service

Delivery Service

Payment Service

Event Bus

The testing pyramid End-to-End Tests

Movie Catalog Service

Authentication Service

Shopping Cart Service

Delivery Service

Payment Service

Event Bus

The testing pyramid End-to-End Tests

Movie Catalog Service

Authentication Service

Shopping Cart Service

Delivery Service

Payment Service

Event Bus

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

To document how to interact with the microservice

To document what the microservice does

To ensure the service work correctly

To ensure we are delivering valuable functionality

Why test our micro-services?

To document how to interact with the microservice

To document what the microservice does

To ensure the service work correctly

To ensure we are delivering valuable functionality

Why test our micro-services?

To document how to interact with the microservice

To document what the microservice does

To ensure the service work correctly

To ensure we are delivering valuable functionality

Why test our micro-services?

To document how to interact with the microservice

To document what the microservice does

To ensure the service work correctly

To ensure we are delivering valuable functionality

Why test our micro-services?

To document how to interact with the microservice

To document what the microservice does

To ensure the service work correctly

To ensure we are delivering valuable functionality

Why test our micro-services?

To document how to interact with the microservice

To document what the microservice does

To ensure the service work correctly

To ensure we are delivering valuable functionality

Why test our micro-services?

“We test our micro-services so we can deploy new features quickly and with confidence”

And with aggressive production monitoring

Writing executable specifications and living documentation

To a sufficient level of confidence

Outside-in testing

Pragmatic web service testing

And with aggressive production monitoring

Writing executable specifications and living documentation

To a sufficient level of confidence

Outside-in testing

Pragmatic web service testing

And with aggressive production monitoring

Writing executable specifications and living documentation

To a sufficient level of confidence

Outside-in testing

Pragmatic web service testing

And with aggressive production monitoring

Writing executable specifications and living documentation

To a sufficient level of confidence

Outside-in testing

Pragmatic web service testing

And with aggressive production monitoring

Writing executable specifications and living documentation

To a sufficient level of confidence

Outside-in testing

Pragmatic web service testing

And with aggressive production monitoring

Writing executable specifications and living documentation

To a sufficient level of confidence

Outside-in testing

Pragmatic web service testing

Outside-in development

To deliver software that matters

And a common language to build a shared understanding

Using examples at multiple levels

Collaborate to discover requirements and identify uncertainty

The essence of BDD

To deliver software that matters

And a common language to build a shared understanding

Using examples at multiple levels

Collaborate to discover requirements and identify uncertainty

The essence of BDD

To deliver software that matters

And a common language to build a shared understanding

Using examples at multiple levels

Collaborate to discover requirements and identify uncertainty

The essence of BDD

To deliver software that matters

And a common language to build a shared understanding

Using examples at multiple levels

Collaborate to discover requirements and identify uncertainty

The essence of BDD

To deliver software that matters

And a common language to build a shared understanding

Using examples at multiple levels

Collaborate to discover requirements and identify uncertainty

The essence of BDD

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

The business owner tells the business

analyst what he wants

12 The business

analyst writes a requirements

document

3 The developer translates the requirements into software

4 The tester translates the requirements

into test cases 5 The technical writer translates

the software into functional and technical

documentationA traditional development process

How does it work?

How does it work?

The business owner and the business

analyst have a conversation about

what he needs.

1

2

3

4 The tester uses these scenarios as the basis for

her tests

5

The automated tests provide feedback on progress and help

document the application

The business analyst, the developer and the tester elaborate the

requirements together.

The scenarios guide the developer and act as

automated tests

They define requirements as

structured, English-language format

"scenarios"

A BDD development process

How does it work?

The business owner and the business

analyst have a conversation about

what he needs.

1

2

3

4 The tester uses these scenarios as the basis for

her tests

5

The automated tests provide feedback on progress and help

document the application

The business analyst, the developer and the tester elaborate the

requirements together.

The scenarios guide the developer and act as

automated tests

They define requirements as

structured, English-language format

"scenarios"

A BDD development process

How does it work?

The business owner and the business

analyst have a conversation about

what he needs.

1

2

3

4 The tester uses these scenarios as the basis for

her tests

5

The automated tests provide feedback on progress and help

document the application

The business analyst, the developer and the tester elaborate the

requirements together.

The scenarios guide the developer and act as

automated tests

They define requirements as

structured, English-language format

"scenarios"

A BDD development process

How does it work?

The business owner and the business

analyst have a conversation about

what he needs.

1

2

3

4 The tester uses these scenarios as the basis for

her tests

5

The automated tests provide feedback on progress and help

document the application

The business analyst, the developer and the tester elaborate the

requirements together.

The scenarios guide the developer and act as

automated tests

They define requirements as

structured, English-language format

"scenarios"

A BDD development process

How does it work?

The business owner and the business

analyst have a conversation about

what he needs.

1

2

3

4 The tester uses these scenarios as the basis for

her tests

5

The automated tests provide feedback on progress and help

document the application

The business analyst, the developer and the tester elaborate the

requirements together.

The scenarios guide the developer and act as

automated tests

They define requirements as

structured, English-language format

"scenarios"

A BDD development process

How does it work?

The business owner and the business

analyst have a conversation about

what he needs.

1

2

3

4 The tester uses these scenarios as the basis for

her tests

5

The automated tests provide feedback on progress and help

document the application

The business analyst, the developer and the tester elaborate the

requirements together.

The scenarios guide the developer and act as

automated tests

They define requirements as

structured, English-language format

"scenarios"

A BDD development process

But what about micro-services?

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

The testing pyramid

Unit Tests

Integration Tests

End-to-end Tests

A different testing pyramid

A different testing pyramid

Executable Business Specifications

A different testing pyramid

Slow-running executable technical specifications

Executable Business Specifications

A different testing pyramid

Fast-running executable technical specifications

Slow-running executable technical specifications

Executable Business Specifications

Outside-in developmentMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Executable Business specs

Outside-in developmentMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Executable Business specs

Scenario: Search movies by director Given the catalog has the following movies: | title | director | actors | | Gladiator | Ridley Scott | Russel Crowe, Joaquin Phoenix | | Letters from Iwo Jima | Clint Eastwood | Ken Watanabe | | Gran Torino | Clint Eastwood | Clint Eastwood, Bee Vang | When I search for movies directed by Clint Eastwood Then I should be presented with the following movies: | title | director | actors | | Letters from Iwo Jima | Clint Eastwood | Ken Watanabe | | Gran Torino | Clint Eastwood | Clint Eastwood, Bee Vang |

Outside-in developmentMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Slow-running technical

specs

Outside-in developmentMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Slow-running technical

specs

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTest("server.port:0") class WhenFindingMoviesViaTheRestAPI extends Specification { @Autowired MovieRepository movieRepository; @Value('${local.server.port}') int port; def "should list movies by director"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, GRAN_TORINO]) when: List<Movie> movies = when().get("/movies/findByDirector/Clint Eastwood").as(List) then: movies.collect {movie -> movie.title} == ["Letters from Iwo Jima", "Gran Torino"] }

Outside-in developmentMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Fast-running

technical specs

Outside-in developmentMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Fast-running

technical specs

def setup() { repository = Mock(MovieRepository) artistService = new ArtistService(movieRepository: repository) } def "should build actor details from films the artist has acted in and has directed"() { given: "Clint Eastwood has directed some films" repository.findByDirector("Clint Eastwood") >> [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] and: "Clint Eastwood has starred in some films" repository.findByActors("Clint Eastwood") >> [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] when: def artistDetails = artistService.findArtistByName("Clint Eastwood") then: artistDetails.isPresent() and: artistDetails.get().name == "Clint Eastwood" && artistDetails.get().filmsActedIn == [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] && artistDetails.get().filmsDirected == [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] }

Spock

Outside-in developmentMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Fast-running

technical specs

Outside-in developmentMovie Catalog Service

Resource

Persistance Layer Gateway

Service Layer

Domain model

Repositories

Fast-running

technical specs

class MovieControllerSpecs extends Specification { MovieRepository movieRepository = Mock() def "should find by director regardless of case (#director -> #filteredDirector)"() { given: def controller = new MovieController(repository: movieRepository) when: controller.findByDirector(director) then: 1*movieRepository.findByDirector(filteredDirector) where: director | filteredDirector "Clint Eastwood" | "Clint Eastwood" "Clint eastwood" | "Clint Eastwood" Spock

Micro-service Contracts

Micro-service ContractsMovie Catalog Service

Resource

Persistance Layer Gateway

Service LayerDomain modelRepositories

Shopping Cart Service

Delivery Service

REST/HTTP

Micro-service ContractsMovie Catalog Service

Resource

Persistance Layer Gateway

Service LayerDomain modelRepositories

Shopping Cart Service

REST/HTTP

Delivery Service

Micro-service ContractsMovie Catalog Service

Resource

Persistance Layer Gateway

Service LayerDomain modelRepositories

Shopping Cart Service

REST/HTTP

Consumer

Delivery Service

Micro-service ContractsMovie Catalog Service

Resource

Persistance Layer Gateway

Service LayerDomain modelRepositories

Shopping Cart Service

REST/HTTP

ConsumerProducer

Delivery Service

Micro-service ContractsMovie Catalog Service

Resource

Persistance Layer Gateway

Service LayerDomain modelRepositories

Shopping Cart Service

REST/HTTP

ConsumerProducer

Contract

Delivery Service

Micro-service ContractsMovie Catalog Service

Resource

Persistance Layer Gateway

Service LayerDomain modelRepositories

Shopping Cart Service

REST/HTTPContract

Delivery Service

Micro-service ContractsMovie Catalog Service

Resource

Persistance Layer Gateway

Service LayerDomain modelRepositories

Shopping Cart Service

Delivery Service

REST/HTTPContract

Mock Shopping

Cart Service

Micro-service Contracts

REST/HTTP

Movie Catalog Service

Web app iPhone app Android app

Micro-service Contracts

REST/HTTP

Movie Catalog Service

Web app iPhone app Android app

Producer

Micro-service Contracts

REST/HTTP

Movie Catalog Service

Web app iPhone app Android app

Consumer

Producer

Micro-service Contracts

REST/HTTP

Movie Catalog Service

Web app iPhone app Android app

Consumer

Producer

Consumer

Micro-service Contracts

REST/HTTP

Movie Catalog Service

Web app iPhone app Android app

Consumer

Producer

Consumer Consumer

Micro-service Contracts

REST/HTTP

Movie Catalog Service

Web app iPhone app Android app

Consumer

Producer

Consumer Consumer

Contract

Micro-service Contracts

REST/HTTP

Movie Catalog Service

Web app iPhone app Android app

Consumer

Producer

Consumer Consumer

Contract

Micro-service Contracts

REST/HTTP

Movie Catalog Service

Web app iPhone app Android app

Consumer

Producer

Consumer Consumer

Contract

Micro-service Contracts

REST/HTTP

Movie Catalog Service

Web app iPhone app Android app

Consumer

Producer

Consumer Consumer

Contract

The Producer API is the sum of the Consumer Contrats

Specialised ToolsPublished specificationsAutomated Acceptance Criteria

Verifying the contracts

Specialised ToolsPublished specificationsAutomated Acceptance Criteria

Verifying the contracts

Specialised ToolsPublished specificationsAutomated Acceptance Criteria

Verifying the contracts

Specialised ToolsPublished specificationsAutomated Acceptance Criteria

Verifying the contracts

Specialised ToolsPublished specificationsAutomated Acceptance Criteria

Verifying the contracts

PactPactoMountebank

More specialised tools

PactPactoMountebank

More specialised tools

PactPactoMountebank

More specialised tools

PactPactoMountebank

More specialised tools

PactPactoMountebank

More specialised tools

PactPactoMountebank

More specialised tools

PactPactoMountebank

More specialised tools

Pact and PactoConsumer Driven Contract Testing

42

Consumer Movie Catalog Service

Pact and PactoConsumer Driven Contract Testing

42

Consumer Movie Catalog Service

Mock Service

Pact help us create and configure a mock service

Pact and PactoConsumer Driven Contract Testing

43

Consumer Movie Catalog Service

Mock Service

Pact and PactoConsumer Driven Contract Testing

43

Consumer Movie Catalog Service

Mock Service

We write specifications to describe the expected

behaviour of the service

Pact and PactoConsumer Driven Contract Testing

44

Consumer Movie Catalog Service

Mock Service

Pact and PactoConsumer Driven Contract Testing

44

Consumer Movie Catalog Service

Mock Service

When we run these specifications, the expected behaviour is recorded in a

‘pact’ file

asdasdasda asdasdasdas asdasdasd asdasdasda

asdasdasdasd asdasdasdasd asdasdasdas

Pact and PactoConsumer Driven Contract Testing

45

Consumer Movie Catalog Service

Mock Service

asdasdasda asdasdasdas asdasdasd asdasdasda

asdasdasdasd asdasdasdasd asdasdasdas

Pact and PactoConsumer Driven Contract Testing

45

Consumer Movie Catalog Service

Mock Service

We can now use this ‘pact’ to test(drive) the service

implementation

asdasdasda asdasdasdas asdasdasd asdasdasda

asdasdasdasd asdasdasdasd asdasdasdas

Levels of confidence

From BDD to TDD

“How much automated testing? As much as you need, but no more.”

Acceptance testing

Acceptance testing

Acceptance testing

Feature: Adding new movies In order to sell more movies As an online movie seller I want to be able to add movies to the catalog Scenario: Adding movies Given the following movie has just come out | title | director | actors | | Jurassic World | Colin Trevorrow | Chris Pratt, Bryce Dallas Howard | When I add this movie to the catalog Then I should be able to find it in the catalog

Feature: Adding new movies In order to sell more movies As an online movie seller I want to be able to add movies to the catalog Scenario: Adding movies Given the following movie has just come out | title | director | actors | | Jurassic World | Colin Trevorrow | Chris Pratt, Bryce Dallas Howard | When I add this movie to the catalog Then I should be able to find it in the catalog

Feature: Adding new movies In order to sell more movies As an online movie seller I want to be able to add movies to the catalog Scenario: Adding movies Given the following movie has just come out | title | director | actors | | Jurassic World | Colin Trevorrow | Chris Pratt, Bryce Dallas Howard | When I add this movie to the catalog Then I should be able to find it in the catalog

@When("I add this movie to the catalog") public void addMovieToCatalog() { movieId = rest().given().contentType("application/json") .content(newMovie) .post("/movies") .then().statusCode(200) .and().extract().jsonPath().getString("id");}

Feature: Adding new movies In order to sell more movies As an online movie seller I want to be able to add movies to the catalog Scenario: Adding movies Given the following movie has just come out | title | director | actors | | Jurassic World | Colin Trevorrow | Chris Pratt, Bryce Dallas Howard | When I add this movie to the catalog Then I should be able to find it in the catalog

Feature: Adding new movies In order to sell more movies As an online movie seller I want to be able to add movies to the catalog Scenario: Adding movies Given the following movie has just come out | title | director | actors | | Jurassic World | Colin Trevorrow | Chris Pratt, Bryce Dallas Howard | When I add this movie to the catalog Then I should be able to find it in the catalog

Feature: Adding new movies In order to sell more movies As an online movie seller I want to be able to add movies to the catalog Scenario: Adding movies Given the following movie has just come out | title | director | actors | | Jurassic World | Colin Trevorrow | Chris Pratt, Bryce Dallas Howard | When I add this movie to the catalog Then I should be able to find it in the catalog

@Then("I should be able to find it in the catalog") public void shouldBeAbleToFindMovieInCatalog() { rest().given().contentType("application/json") .get("/movies/{movieId}", movieId) .then().statusCode(200) .and().body("title", equalTo(newMovie.getTitle())) .and().body("director", equalTo(newMovie.getDirector()));}

Feature: Adding new movies In order to sell more movies As an online movie seller I want to be able to add movies to the catalog Scenario: Adding movies Given the following movie has just come out | title | director | actors | | Jurassic World | Colin Trevorrow | Chris Pratt, Bryce Dallas Howard | When I add this movie to the catalog Then I should be able to find it in the catalog

@Then("I should be able to find it in the catalog") public void shouldBeAbleToFindMovieInCatalog() { rest().given().contentType("application/json") .get("/movies/{movieId}", movieId) .then().statusCode(200) .and().body("title", equalTo(newMovie.getTitle())) .and().body("director", equalTo(newMovie.getDirector()));}

Rest Assured + Serenity

Feature: Adding new movies In order to sell more movies As an online movie seller I want to be able to add movies to the catalog Scenario: Adding movies Given the following movie has just come out | title | director | actors | | Jurassic World | Colin Trevorrow | Chris Pratt, Bryce Dallas Howard | When I add this movie to the catalog Then I should be able to find it in the catalog

@Then("I should be able to find it in the catalog") public void shouldBeAbleToFindMovieInCatalog() { rest().given().contentType("application/json") .get("/movies/{movieId}", movieId) .then().statusCode(200) .and().body("title", equalTo(newMovie.getTitle())) .and().body("director", equalTo(newMovie.getDirector()));}

Testing REST services with Rest-Assured

Testing REST services with Rest-Assuredget(“/movies/findByTitle/Gladiator") .then().body("title", equalTo("Gladiator"));

Testing REST services with Rest-Assuredget(“/movies/findByTitle/Gladiator") .then().body("title", equalTo("Gladiator"));

Testing REST services with Rest-Assured

when().get(“/movies/findByTitle/Gladiator”) .then().body("nominations.category", hasItems("Best Actor", "Special Effects"));

Testing REST services with Rest-Assured

when().get(“/movies/findByTitle/Gladiator”) .then().body("nominations.category", hasItems("Best Actor", "Special Effects"));

Testing REST services with Rest-Assured

given().contentType("application/json") .content(newMovie) .post("/movies") .then().statusCode(200) .and().extract().jsonPath().getString("id");

Testing REST services with Rest-Assured

given().contentType("application/json") .content(newMovie) .post("/movies") .then().statusCode(200) .and().extract().jsonPath().getString("id");

Posting data

Testing REST services with Rest-Assured

Movie matchingMovie = get("/movies/findByTitle/Gladiator").as(Movie.class);

Testing REST services with Rest-Assured

Movie matchingMovie = get("/movies/findByTitle/Gladiator").as(Movie.class);

Convert responses to Java objects

Testing REST services with Rest-Assuredgiven().param("title","Unknown"). when().get("/movies/search"). then().statusCode(404);

Testing REST services with Rest-Assuredgiven().param("title","Unknown"). when().get("/movies/search"). then().statusCode(404);

Check for errors

Resource-level testing

Resource-level testing

Spock

Resource-level testing

Spock

Resource-level testing

Spock

57

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTest("server.port:0") class WhenFindingMovies extends Specification { @Autowired MovieRepository movieRepository; @Value('${local.server.port}') int port; def setup() { movieRepository.deleteAll(); RestAssured.port = port; } def "should list all movies"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, GRAN_TORINO]) when: def movies = when().get("/movies").as(List) then: movies.collect {movie -> movie.title} == ["Gladiator", "Letters from Iwo Jima", "Gran Torino"] }

57

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTest("server.port:0") class WhenFindingMovies extends Specification { @Autowired MovieRepository movieRepository; @Value('${local.server.port}') int port; def setup() { movieRepository.deleteAll(); RestAssured.port = port; } def "should list all movies"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, GRAN_TORINO]) when: def movies = when().get("/movies").as(List) then: movies.collect {movie -> movie.title} == ["Gladiator", "Letters from Iwo Jima", "Gran Torino"] }

Spring Context

57

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTest("server.port:0") class WhenFindingMovies extends Specification { @Autowired MovieRepository movieRepository; @Value('${local.server.port}') int port; def setup() { movieRepository.deleteAll(); RestAssured.port = port; } def "should list all movies"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, GRAN_TORINO]) when: def movies = when().get("/movies").as(List) then: movies.collect {movie -> movie.title} == ["Gladiator", "Letters from Iwo Jima", "Gran Torino"] }

Spring Integration Tests

57

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTest("server.port:0") class WhenFindingMovies extends Specification { @Autowired MovieRepository movieRepository; @Value('${local.server.port}') int port; def setup() { movieRepository.deleteAll(); RestAssured.port = port; } def "should list all movies"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, GRAN_TORINO]) when: def movies = when().get("/movies").as(List) then: movies.collect {movie -> movie.title} == ["Gladiator", "Letters from Iwo Jima", "Gran Torino"] }

Rest Assured

57

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTest("server.port:0") class WhenFindingMovies extends Specification { @Autowired MovieRepository movieRepository; @Value('${local.server.port}') int port; def setup() { movieRepository.deleteAll(); RestAssured.port = port; } def "should list all movies"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, GRAN_TORINO]) when: def movies = when().get("/movies").as(List) then: movies.collect {movie -> movie.title} == ["Gladiator", "Letters from Iwo Jima", "Gran Torino"] }

Controller-level testing

Controller-level testing

Spock

Controller-level testing

Spock

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTestclass WhenViewingActorDetailsViaTheController extends Specification { @Autowired ArtistController artistController; def "should retrieve actor details for an artist"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO]) when: Artist artist = artistController.findArtistDetails("Clint Eastwood") then: artist.name == "Clint Eastwood" and: artist.filmsActedIn.containsAll(THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO) and: artist.filmsDirected.containsAll(LETTERS_FROM_IWO_JIMA, GRAN_TORINO) }

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTestclass WhenViewingActorDetailsViaTheController extends Specification { @Autowired ArtistController artistController; def "should retrieve actor details for an artist"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO]) when: Artist artist = artistController.findArtistDetails("Clint Eastwood") then: artist.name == "Clint Eastwood" and: artist.filmsActedIn.containsAll(THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO) and: artist.filmsDirected.containsAll(LETTERS_FROM_IWO_JIMA, GRAN_TORINO) }

Use a fully wired controller

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTestclass WhenViewingActorDetailsViaTheController extends Specification { @Autowired ArtistController artistController; def "should retrieve actor details for an artist"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO]) when: Artist artist = artistController.findArtistDetails("Clint Eastwood") then: artist.name == "Clint Eastwood" and: artist.filmsActedIn.containsAll(THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO) and: artist.filmsDirected.containsAll(LETTERS_FROM_IWO_JIMA, GRAN_TORINO) } Use domain objects rather than REST calls

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MovieServiceApplication.class) @WebAppConfiguration@IntegrationTestclass WhenViewingActorDetailsViaTheController extends Specification { @Autowired ArtistController artistController; def "should retrieve actor details for an artist"() { given: movieCatalogContains([GLADIATOR, LETTERS_FROM_IWO_JIMA, THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO]) when: Artist artist = artistController.findArtistDetails("Clint Eastwood") then: artist.name == "Clint Eastwood" and: artist.filmsActedIn.containsAll(THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO) and: artist.filmsDirected.containsAll(LETTERS_FROM_IWO_JIMA, GRAN_TORINO) }

Service or domain-level testing

Service or domain-level testing

Spock

MovieRepository repositorydef artistServicedef setup() { repository = Mock(MovieRepository) artistService = new ArtistService(movieRepository: repository) } def "should build actor details from films the artist has acted in and has directed"() { given: "Clint Eastwood has directed some films" repository.findByDirector("Clint Eastwood") >> [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] and: "Clint Eastwood has starred in some films" repository.findByActors("Clint Eastwood") >> [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] when: def artistDetails = artistService.findArtistByName("Clint Eastwood") then: artistDetails.isPresent() and: artistDetails.get().name == "Clint Eastwood" && artistDetails.get().filmsActedIn == [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] && artistDetails.get().filmsDirected == [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] }

MovieRepository repositorydef artistServicedef setup() { repository = Mock(MovieRepository) artistService = new ArtistService(movieRepository: repository) } def "should build actor details from films the artist has acted in and has directed"() { given: "Clint Eastwood has directed some films" repository.findByDirector("Clint Eastwood") >> [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] and: "Clint Eastwood has starred in some films" repository.findByActors("Clint Eastwood") >> [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] when: def artistDetails = artistService.findArtistByName("Clint Eastwood") then: artistDetails.isPresent() and: artistDetails.get().name == "Clint Eastwood" && artistDetails.get().filmsActedIn == [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] && artistDetails.get().filmsDirected == [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] }

Mocked repositories

MovieRepository repositorydef artistServicedef setup() { repository = Mock(MovieRepository) artistService = new ArtistService(movieRepository: repository) } def "should build actor details from films the artist has acted in and has directed"() { given: "Clint Eastwood has directed some films" repository.findByDirector("Clint Eastwood") >> [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] and: "Clint Eastwood has starred in some films" repository.findByActors("Clint Eastwood") >> [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] when: def artistDetails = artistService.findArtistByName("Clint Eastwood") then: artistDetails.isPresent() and: artistDetails.get().name == "Clint Eastwood" && artistDetails.get().filmsActedIn == [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] && artistDetails.get().filmsDirected == [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] }

Call the service

MovieRepository repositorydef artistServicedef setup() { repository = Mock(MovieRepository) artistService = new ArtistService(movieRepository: repository) } def "should build actor details from films the artist has acted in and has directed"() { given: "Clint Eastwood has directed some films" repository.findByDirector("Clint Eastwood") >> [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] and: "Clint Eastwood has starred in some films" repository.findByActors("Clint Eastwood") >> [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] when: def artistDetails = artistService.findArtistByName("Clint Eastwood") then: artistDetails.isPresent() and: artistDetails.get().name == "Clint Eastwood" && artistDetails.get().filmsActedIn == [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] && artistDetails.get().filmsDirected == [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] }

Check the outcomes

MovieRepository repositorydef artistServicedef setup() { repository = Mock(MovieRepository) artistService = new ArtistService(movieRepository: repository) } def "should build actor details from films the artist has acted in and has directed"() { given: "Clint Eastwood has directed some films" repository.findByDirector("Clint Eastwood") >> [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] and: "Clint Eastwood has starred in some films" repository.findByActors("Clint Eastwood") >> [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] when: def artistDetails = artistService.findArtistByName("Clint Eastwood") then: artistDetails.isPresent() and: artistDetails.get().name == "Clint Eastwood" && artistDetails.get().filmsActedIn == [THE_GOOD_THE_BAD_AND_THE_UGLY, GRAN_TORINO] && artistDetails.get().filmsDirected == [LETTERS_FROM_IWO_JIMA, GRAN_TORINO] }

Living Documentation

Living Documentation

Living Functional Documentation with Serenity and Cucumber

65

Scenario: Adding movies Given the following movie has just come out | title | director | actors | | Jurassic World | Colin Trevorrow | Chris Pratt, Bryce Dallas Howard | When I add this movie to the catalog Then I should be able to find it in the catalog

Executable Specifications

Automated tests Living documentation

66

66

Context

66

Context

Narrative

67

67

Technical details

Living API Documentation with Swagger

Springfox

69

YAML Specifications Documentation website

Client libraries Test stubs

70

@RequestMapping(method = GET) @ApiOperation("List all the movies in the catalog") public List<Movie> findAll() { return repository.findAll();} @RequestMapping(method = POST) @ApiOperation(value = "Add a new movie to the catalog", httpMethod = "POST") public Movie add(@RequestBody Movie newMovie) { return repository.save(newMovie);} @RequestMapping(value = "/{id}", method = DELETE) @ApiOperation(value = "Removing a movie from the catalog", httpMethod = "DELETE") public void delete(@PathVariable String id) { repository.delete(id);}

Springfox

Some annotations…

Swagger and Spring Boot

70

@RequestMapping(method = GET) @ApiOperation("List all the movies in the catalog") public List<Movie> findAll() { return repository.findAll();} @RequestMapping(method = POST) @ApiOperation(value = "Add a new movie to the catalog", httpMethod = "POST") public Movie add(@RequestBody Movie newMovie) { return repository.save(newMovie);} @RequestMapping(value = "/{id}", method = DELETE) @ApiOperation(value = "Removing a movie from the catalog", httpMethod = "DELETE") public void delete(@PathVariable String id) { repository.delete(id);}

Springfox

Some annotations…

Swagger and Spring Boot

70

@RequestMapping(method = GET) @ApiOperation("List all the movies in the catalog") public List<Movie> findAll() { return repository.findAll();} @RequestMapping(method = POST) @ApiOperation(value = "Add a new movie to the catalog", httpMethod = "POST") public Movie add(@RequestBody Movie newMovie) { return repository.save(newMovie);} @RequestMapping(value = "/{id}", method = DELETE) @ApiOperation(value = "Removing a movie from the catalog", httpMethod = "DELETE") public void delete(@PathVariable String id) { repository.delete(id);}

Springfox

Some annotations…

Swagger and Spring Boot

70

@RequestMapping(method = GET) @ApiOperation("List all the movies in the catalog") public List<Movie> findAll() { return repository.findAll();} @RequestMapping(method = POST) @ApiOperation(value = "Add a new movie to the catalog", httpMethod = "POST") public Movie add(@RequestBody Movie newMovie) { return repository.save(newMovie);} @RequestMapping(value = "/{id}", method = DELETE) @ApiOperation(value = "Removing a movie from the catalog", httpMethod = "DELETE") public void delete(@PathVariable String id) { repository.delete(id);}

Springfox

Some annotations…

Swagger and Spring Boot

71

Springfox

@EnableAutoConfiguration@EnableSwagger2public class MovieServiceApplication { public static void main(String[] args) { SpringApplication.run(MovieServiceApplication.class, args); } @Bean public Docket moviesApi() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build() .pathMapping("/") .directModelSubstitute(LocalDate.class, String.class) .genericModelSubstitutes(ResponseEntity.class); }} Some configuration…

Swagger and Spring Boot

72

Springfox

Documentationbundled with your

web services

Swagger and Spring Boot

Production monitoring

Automated MonitoringFast provisioningA fast deployment pipeline

So you want to micro-service?

Automated health checksReal operationsSmoke tests in production

Simple checks may not be enough

An example with Spring Actuator@Componentpublic class CatalogHealth implements HealthIndicator { @Autowired MovieRepository movieRepository; @Override public Health health() { return (isCatalogStocked()) ? Health.status(new Status("UP", "Movie count: " + movieRepository.count())).build() : Health.down().build(); } private boolean isCatalogStocked() { return (movieRepository.count() > 0); }}

An example with Spring Actuator@Componentpublic class CatalogHealth implements HealthIndicator { @Autowired MovieRepository movieRepository; @Override public Health health() { return (isCatalogStocked()) ? Health.status(new Status("UP", "Movie count: " + movieRepository.count())).build() : Health.down().build(); } private boolean isCatalogStocked() { return (movieRepository.count() > 0); }}

A Spring Actuator Health Check

An example with Spring Actuator@Componentpublic class CatalogHealth implements HealthIndicator { @Autowired MovieRepository movieRepository; @Override public Health health() { return (isCatalogStocked()) ? Health.status(new Status("UP", "Movie count: " + movieRepository.count())).build() : Health.down().build(); } private boolean isCatalogStocked() { return (movieRepository.count() > 0); }}

A Spring Actuator Health Check

Custom logic to decide if the app is working

An example with Spring Actuator@Componentpublic class MovieTransactionHealth implements HealthIndicator { @Autowired MovieController movieController; @Override public Health health() { return Health.status(transactionStatus()).build(); } private Status transactionStatus() { try { Movie movie = new Movie("TEST title", "TEST description", "TEST director", ImmutableList.of("TEST actor")); Movie reloadedMovie = movieController.add(movie); movieController.delete(reloadedMovie.getId()); return Status.UP; } catch (Throwable e) { return new Status("DOWN", e.toString()); } }}

An example with Spring Actuator@Componentpublic class MovieTransactionHealth implements HealthIndicator { @Autowired MovieController movieController; @Override public Health health() { return Health.status(transactionStatus()).build(); } private Status transactionStatus() { try { Movie movie = new Movie("TEST title", "TEST description", "TEST director", ImmutableList.of("TEST actor")); Movie reloadedMovie = movieController.add(movie); movieController.delete(reloadedMovie.getId()); return Status.UP; } catch (Throwable e) { return new Status("DOWN", e.toString()); } }}

More complex custom logic

An example with Spring Actuator

@Componentpublic class MovieTransactionHealth implements HealthIndicator { @Autowired MovieController movieController; @Override public Health health() { return Health.status(transactionStatus()).build(); } private Status transactionStatus() {…} }

@Componentpublic class CatalogHealth implements HealthIndicator { @Autowired MovieRepository movieRepository; @Override public Health health() { return (isCatalogStocked()) ? Health.status(new Status("UP", "Movie count: " + movieRepository.count())).build() : Health.down().build(); } private boolean isCatalogStocked() {…} }

Try all this out at home!

Install Gradle

Install MongoDB

Clone the repowakaleo/movie-service

Questions?

John Ferguson Smartjohn.smart@wakaleo.com

wakaleohttp://www.wakaleo.com

top related