Once you have your Microservices setup, the most pertinent question is how to I test Microservices and ensure that all the moving parts of this distributed system stay in sync.
The presentation provides testing strategies on how to test Microservices and provides focussed understanding of using Consumer Driven Contracts (CDC) to test Microservices API. Additionally it provides pointers around how to do debug Microservices and trace the performance of individual services.
Please read the following presentations before referencing "Testing Microservices"
1. Introduction to Microservices - https://www.slideshare.net/anilallewar/introduction-to-microservices-78270318
2. Build the Microservices sample application -
https://www.slideshare.net/anilallewar/building-microservices-sample-application
5. Testing Services
S1
S2 S3
S4
1. Deploy all Microservices and
perform end-to-end tests
Testing Approach
1. Simulates production
2. Real communication
1. Too cumbersome – deploy DB,
all services
2. Late feedback
3. Environment blocked
4. Very hard to debug
6. Testing Services
S1
2. Mocking other Microservices in
unit/integration tests
Testing Approach
1. Fast feedback
2. No infrastructure setup
1. Stubs might be different
than the real service
2. Tests will pass but the
application might fail in
production
7. What do we need?
Producer
•Define contract
•Verify that API implementation is per contract – server tests
•Publish stub versions to repo – changes visible to both sides
Consumer
•Mock producer based on stub definitions – client integration
tests
•ATDD – Acceptance TDD
9. Spring Cloud Contract Verifier
Contract Definition Language (DSL)
• Definition either in groovy or pact format
Used to produce following resources
• JSON stub definitions
Used by client side
Test written by hand, test data provided by Spring
• Server tests
Test generated by Spring
12. Client tests
Actual test
Fails for 2 reasons
• Call is secured though OAuth2 access token
• Stub definition not available
@Test
public void testGetCommentsForTask() {
CommentCollectionResource comments =
this.commentsService.getCommentsForTask(REQUEST_TASK_ID);
Assert.assertEquals(2, comments.getTaskComments().size());
Assert.assertTrue(comments.getTaskComments().get(0).getTaskId().equals(TEST_TASK_ID));
}
14. Run tests
Once you have the MockOAuth2 token code
• Comment the @AutoConfigureStubRunner annotation
• Run the test with ./gradlew clean build
java.lang.AssertionError: expected:<2> but was:<1>
• Circuit breaker kicked in and provided default value
Uncomment @AutoConfigureStubRunner and run the test
• Error creating bean with name 'batchStubRunner'
16. Build script – Producer (comments-webservice)
// Setup the package which contains base classes that the spring cloud contract test case would extend
contracts {
packageWithBaseClasses = 'com.anilallewar.microservices.comments.contracts'
}
clean.doFirst {
delete "~/.m2/repository/anilallewar/basic-comments-webservice"
delete "~/.m2/repository/anilallewar/basic-comments-webservice-stubs"
}
// Setup the artifact id with which the stubs jar would be published, the group id comes from the 'group' attribute defined earlier
publishing {
publications {
mavenJava(MavenPublication) {
artifactId jar.baseName
version jar.version
from components.java
}
stubs(MavenPublication) {
artifactId "${jar.baseName}-stubs"
version jar.version
artifact verifierStubsJar
}
}
}
18. Abstract Base Class
Generated test extends the defined base class
Since the package for base classes is defined
as com.anilallewar.microservices.comments.contracts and
the contract is defined under the task/comments folder
under test/resources/contracts, the base class name is
TaskCommentsBase
Add @Ignore annotation – not a test class
19. Abstract base class
Inject a WebApplicationContext so that Spring dependency is
initialized correctly.
Annotations
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { CommentsApplication.class }, webEnvironment
= WebEnvironment.MOCK, properties = {
"spring.cloud.discovery.enabled=false", "spring.cloud.config.enabled=false"
})
20. Run tests
Run the test with ./gradlew clean build
•This will generate the server tests under /comments-
webservice/build/generated-test-
sources/contracts/org/springframework/cloud/contract/verifier
/tests
Publish the contract stub using ./gradlew
publishToMavenLocal
Once the contract is published, go to the task-webservice
folder and run ./gradlew clean build
•This time around the test should pass
22. Sleuth & Zipkin
Sleuth
• Attaches unique id to request as it passes through enabled services
• Logs can be mined using aggregation tools like ELK (ElasticSearch,
Logstash and Kibana)
Zipkin
• Distributed tracing system
• Gathers timing data across Microservices – collection and lookup
23. Dependencies
Create a new project by visiting https://start.spring.io/
• Gradle project –> with Java and Spring Boot 1.5.4
• Group -> com.<name>.microservices
• Artifact -> tracing
• Dependencies -> Zipkin UI, Zipkin Client
Additional dependency
• compile('io.zipkin.java:zipkin-server')
Run ./gradlew clean build eclipse
26. View Traces
Run all the Microservices and other servers (zipkin server,
web-portal etc)
Hit http://localhost:9411/ (Zipkin base url)
UI
• Find specific trace and timing information
• Service dependency graph
Trace id is added to logs – enables distributed log analysis
Run the test with the SpringRunner class instead of standard Junit runner
Properties injected into spring boot environment
Disable discovery and configuration from external sources
Map the stubs present in the “basic-comments-webservice-stubs” repo to the “comments-webservice” service
Setup OAuth2 client configuration
Configure the stubs defined in the “basic-comments-webservice-stubs” jar file that is under the “anilallewar” group under any version and run the stub server on port 9083
Remove the Spring application context after the test