Our software becomes more complex with every iteration we spent on it.
There are new systems we need to integrate with or if you're working in
microservice eco-system there are dozens of new services in your
environment every week (especially at the beginning the growth is rapid).
When it comes to testing entire system on end-2- end basis, so far it
seemed we had only 2 choices:
1. Deploy the system with a prod-like configuration (with all the
collaborators) and perform heavy manual tests followed by heavy manual
regression tests... but as Maynard James Keenanaas sings "Boredom's
not a burden anyone should bare" - anyone who's been doing manual
regression tests knows exactly what I (and Maynard ;-) am talking about
2. Deploy the system with a prod-like configuration (similarly as above)
and automate all the end-2- end tests that will eventually end-up as a
regression test suite
Consumer driven contract gives us a third option:
3. Establish contracts with all the collaborators and test your service in
isolation using contracts whenever it comes to contacting an external
system, regardless of whether it is developed within the company or
outside (I must admit that the former case requires some more
orchestration on managerial level but it is doable)
This workshop will give you a hands-on experience. You will create two
REST services integrated with each other, and implement an API change
using consumer contracts. This approach will allow us to do the change in
both services in separation, in a BDD style.
2. Questions
• What problem we’re trying to solve?
• How the problem were solved so far?
• How we want to solve it with CDC?
• What other problems do we introduce?
4. Deploy all services in
one environment
Automated or
Manual
Production
similarity
Environment
stability
Test analysis
Feedback loop
Environment as
bottleneck
Proper versions
Test isolation
Environment
Maintenance
Manual
Cost (low only in
small scales at
the beginning)
5. Deploy single service
with mocks
Production
similarity
Environment
Stability
Test analysis
Feedback loop
Environment as
bottleneck
Proper versions
Test isolation
Environment
maintenance
Cost
9. How to do it with CDC ?
• Prove that mocks are alike production
• If collaborator behaviour changes all parties should be informed
immediately
• Generate all the boilerplate code
11. How?
1. Define problem
2. Define contract
3. Generate STUBs for Client (Consumer)
4. Use STUBs forClient’s tests
5. Generate tests for Server (Producer)
12. 1. Define problem & prepare project
• https://start.spring.io/
• WEB
• Cloud ContractVerifier
• Cloud Contract Stub Runner
Trip Advisor
Hotel Advisor
Car rental
Advisor
Ticket
booking
Advisor
13. 1. Define problem & prepare project
• https://start.spring.io/
• WEB
• Cloud ContractVerifier
• Cloud Contract Stub Runner
Trip Advisor
Hotel Advisor
Car rental
Advisor
Ticket
booking
Advisor
15. 1. Contract description
Trip Advisor Hotel Advisor
GET /hotels?location=Krakow
{
"availableHotels": [
{
"id": "779",
"name": "Sheraton",
"address": "Kraków, ul. Jana Nowaka 1”
},
{
"id": "892",
"name": "Novotel",
"address": "Kraków, ul. Długa 5”
}
]
}
16. 1. Contract
• Ignoring contract
• Passing value from file (starting from version
1.2.0)
• Executing method available in base class
• Creating response basing on data from requestContract.make {
ignored()
}
response {
status 200
body (execute("commandName(${fromRequest().body('some/string/path')})"))
headers {
contentType(applicationJson())
}
}
17. 2. Generate STUBs
• Contract verifier plugin
• Generate and run tests
• Create and install STUBs
$> mvn clean install -DskipTests
20. $> mvn help:describe -Dplugin=org.springframework.cloud:spring-cloud-contract-maven-plugin
Name: Spring Cloud Contract Maven Plugin
Description: Spring Cloud Contract Maven Plugin
Group Id: org.springframework.cloud
Artifact Id: spring-cloud-contract-maven-plugin
Version: 1.1.2.RELEASE
Goal Prefix: spring-cloud-contract
This plugin has 5 goals:
spring-cloud-contract:convert
Convert Spring Cloud Contract Verifier contracts into WireMock stubs mappings.
This goal allows you to generate `stubs-jar` or execute
`spring-cloud-contract:run` with generated WireMock mappings.
spring-cloud-contract:generateStubs
Picks the converted .json files and creates a jar. Requires convert to be
executed first
spring-cloud-contract:generateTests
From the provided directory with contracts generates the acceptance tests on
the producer side
spring-cloud-contract:help
Display help information on spring-cloud-contract-maven-plugin.
Call mvn spring-cloud-contract:help -Ddetail=true -Dgoal=<goal-name> to
display parameter details.
spring-cloud-contract:run
22. 4. Generate tests for Server (Producer)
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {ContractTestConfiguration.class})
public abstract class HotelsBase {
@Autowired HotelAdvisorService hotelAdvisorService;
@Before
public void setup() {
RestAssuredMockMvc.standaloneSetup(
new HotelAdvisorController(hotelAdvisorService));
}
}
23. APIVersioning
• Semantic versioning
• Keep contracts for each version of API
• Consider maven classifiers to mark production/dev versions (mind that it
doesn’t match the original idea behind classifier concept)
• Keep contracts in separate module/repository (different release cycle)
24. Defer breaking change
• Tolerant reader pattern
• Expand and contract pattern (3 steps to unbreake API change)
• 6 steps to unbreak DB schema change in continous deployment
environment (if you’re not doing continuous deployment you can simplify
the algorithm and have only 5 steps)
26. 5 steps algorithm
• 0. Service (v1.0) using old data
structure
• 1. Add new data structure (1st
DB change) incompatible with
the old one (omitt constrainsts)
27. 5 steps algorithm
• 2. Deploy new version (v1.1) of
your service reading from old
structure and writing to old and
new
28. 5 steps algorithm
• 3. Migrate all old data to new
structure (2nd DB change) and
add constraints to new data
structure
29. 5 steps algorithm
• 4. Release new version of your
service (v1.2) reading from new
and writing to new data
structure
30. 5 steps algorithm
• 4. Clean old data structures (3rd
DB change)
2 service releases
3 DB changes
31. 6 steps algorithm
• 0. Service (v1.0) using old data
structure
• 1. Deploy new version of your
service (v1.1) marking record
that it created/updated (not
needed if not doing continuous
deployment)
32. 6 steps algorithm
• 2. Add new data structure (1st
DB schema change)
incompatible with the old one
(omitt constrainsts)
33. 6 steps algorithm
• 3. Deploy new version of your service
(1.2) with quite complex (simple if not
doing continuous deployment) data
creation/updating logic:
• Create new data in old and new
structure
• If old data is not marked and new data
does not exist, read from old and write
to old and new
• If old data is not marked and new data
exists, read from new and write to old
and new
• If old data is marked, read from old and
write to old and new and remove marker
34. 6 steps algorithm
• 4. Migrate old data in case when
new data does not exist or old
data is marked and add
constraints to new data
structure
35. 6 steps algorithm
• 5. Deploy new version (1.3) of
your service reading from new
and writing to new data
structure
Environment stability
- usually machines in such test environment are much weaker then in production or much higher burden is put on them
- since we have hundreds of services, each is usually deployed independently hence you never know what is trully currently deployed on environment
Proper versions
- how to ensure that your service is tested against proper versions of other services (the ones that are deployed on prod) in such dynamic environment
Environment as a bottleneck
- Proper versions – one would have to deploy newest version of his service and ask to freeze the environment for the time of tests
- test isolation – one would have to ask other teams no to perform their tests in order not to interfere
Test analysis
- if a test fails, you don’t really know which service failed, finding root cause is a nightmare
Environment stability
- usually machines in such test environment are much weaker then in production or much higher burden is put on them
- since we have hundreds of services, each is usually deployed independently hence you never know what is trully currently deployed on environment
Proper versions
- how to ensure that your service is tested against proper versions of other services (the ones that are deployed on prod) in such dynamic environment
Environment as a bottleneck
- Proper versions – one would have to deploy newest version of his service and ask to freeze the environment for the time of tests
- test isolation – one would have to ask other teams no to perform their tests in order not to interfere
Test analysis
- if a test fails, you don’t really know which service failed, finding root cause is a nightmare
groupId:artifactId:version:classifier:port
If you don’t provide the port then a random one will be picked
If you don’t provide the classifier then the default one will be taken.
If you don’t provide the version then the + will be passed and the latest one will be downloaded
Configuration used to find a base class for all generated tests. The goal of the base class is to run the server under test.
Classifier - The classifier allows to distinguish artifacts that were built from the same POM but differ in their content