Sunteți pe pagina 1din 28

Testing Microservices

Types of Testing

API Service
Stubs
Microservice Contracts
Service Client

Domain External Service


Microservice Contracts
Persistence Unit Test
Integration Test
Database Component Test
End-to-end Test
Test Pyramid
Manual
Exploratory
Selenium, Appium, Sikuli,
End to end JBehave

Component / Spring Cloud Contract, Pact,


Cucumber
Contracts
Junit, SOAPUI
Integration
Junit, Nunit, Mockito, JMock,
Unit (Solitary / Sociable) Lambda Behave
Consumer Driven
Contracts
Testing Services
Testing Approach
S1 1. Deploy all Microservices and
perform end-to-end tests

1. Simulates production
2. Real communication
S2 S3

1. Too cumbersome – deploy DB,


all services
2. Late feedback
S4 3. Environment blocked
4. Very hard to debug
Testing Services
Testing Approach
2. Mocking other Microservices in
S1
unit/integration tests

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
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
Consumer Driven Contracts
Consumer Client Test
Integration Test
Acceptance
Tests

Producer Repository
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
Build script – Consumer (task-webservice)
dependencies
• testCompile("org.springframework.security:spring-security-test")
• testCompile("org.springframework.cloud:spring-cloud-starter-
contract-stub-runner")
Client tests
Create integration test
• Annotations
@RunWith(SpringRunner.class) // 1
@SpringBootTest(webEnvironment = WebEnvironment.MOCK, properties = {
"spring.cloud.discovery.enabled=false",
"spring.cloud.config.enabled=false","stubrunner.idsToServiceIds.basic-comments-webservice-
stubs=comments-webservice" }). // 2
@Import(OAuth2ClientTestConfiguration.class). // 3
@AutoConfigureStubRunner(ids={"anilallewar:basic-comments-webservice-stubs:+:stubs:9083"},
workOffline=true) // 4
@DirtiesContext // 5
Client tests
Actual test
@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));
}

Fails for 2 reasons


• Call is secured though OAuth2 access token
• Stub definition not available
Mock OAuth2 token
Create SecurityContext holding the mock access token
• https://spring.io/blog/2014/05/07/preview-spring-security-test-
method-security#withsecuritycontext
Code
• https://github.com/anilallewar/microservices-basics-spring-
boot/tree/master/task-
webservice/src/test/java/com/anilallewar/microservices/task
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'
Build script – Producer (comments-webservice)
buildscript
• project.ext
 springCloudContractVesrion = '1.1.1.RELEASE'
• dependencies
 classpath "org.springframework.cloud:spring-cloud-contract-gradle-
plugin:${project.springCloudContractVesrion}”

dependencies
• testCompile('org.springframework.cloud:spring-cloud-starter-
contract-verifier')
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
}
}
}
Contract DSL
Src/test/resources/contracts
response {
request { status 200
method 'GET' body( [
url $(consumer(regex('/comments/([0-9a- [
zA-z]+)')), producer('/comments/task11')) "taskId": "task11",
headers { "comment": "comment on task11",
accept(applicationJson()) "posted": $(consumer('2015-04-
} 23'),producer(execute('convertTimeValueToDate($it)')))
} ]]
)
headers {
contentType('application/json')
}
}
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
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"
})
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
Distributed Tracing
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
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
Code changes
Application.properties
• server.port=9411
Alternatively you can add configuration on the centralized
configuration server
Add @EnableZipkinServer annotation on the Spring boot
application class
Add Zipkin client properties
Check below properties exists in configuration files for “api-
gateway”, “comments-webservice”, “task-webservice” and
“user-webservice”
spring:
zipkin:
baseUrl: http://localhost:9411/
sleuth:
sampler:
percentage: 1.0
sample:
zipkin:
enabled: true
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
Sample Trace
Contact Me
anilallewar@yahoo.co.in

https://www.linkedin.com/in/anilallewar

https://github.com/anilallewar

http://www.slideshare.net/anilallewar

@anilallewar

S-ar putea să vă placă și