Testing With Spring Cloud Contract
Find out more about the consumer/provider relationship and how to test with Spring Cloud contract; testing and validating the API communications.
Join the DZone community and get the full member experience.
Join For FreeWith many industries transforming to a microservices architecture, testing these microservices is a challenge. Different teams own different services, so testing and validating the API communications between these microservices is important.
The major drawback of using Test API frameworks requires all your microservices to be up and running.
For example, if your project has four services A, B, C, and D.
You may also like: Getting Started With Spring Cloud Stream
Testing a method in Service A, calls service B then Service C and finally Service D. If any of these services (B, C, and D) are down or not working as expected then your test method (for service A) failed. Though this scenario is good for your end-to-end testing, it may not be ideal for functional testing of your service A.
How to overcome this? Yes, you are right! Mock the call from Service A to Service B.
In this blog, we assume your microservices written in Spring Boot 2. Let consider mocking the service with "Spring Cloud Contract".
To perform our use case, we have two major steps:
1. Provider Verifier setup (Service B).
2. Consumer (Service A) setup.
Provider Verifier Setup
Before proceeding with the Spring Cloud Contract, we need to mark the dependencies. In Gradle, add the following classpath entries to your build script section (referenced globally in your project) of build.gradle.
buildscript{
dependencies{
classpath "org.springframework.cloud:spring-cloud
"-contract-gradle-plugin:2.1.3.RELEASE"
classpath "io.rest-assured:rest-assured:3.0.2"
classpath "io.rest-assured:spring-mock-mvc:3.0.2"
}
}
The next step is to apply the plugins. Used maven-publish here, so I can generate artifacts and publish to repository either local or remote as appropriate to your project.
apply plugin: 'spring-cloud-contract'
apply plugin: 'maven-publish'
Next, add your project dependencies
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:2.1.3.RELEASE"
}
}
You can override default spring contract settings, here I have modified the base path of my Java class, which needs to be created for our testing.
contracts {
packageWithBaseClasses = 'com.mypackage.naveen.serviceB'
generatedTestSourcesDir = project.file('src/generatedContract')
}
Finally, add testCompile dependency.
testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
Do Gradle clean and build and ensure all your dependencies are downloaded and your project is built successfully.
Time to write our first contract. You can define your contract in YAML or groovy.
What's our purpose here? We have to mock.
Create a file called "ContractForServiceAConsumer.groovy" under src/test/resources/contracts.
Contract.make {
request {
method 'POST'
url '/testservice'
body(file("request.json"))
headers {
contentType(applicationJson())
}
}
response {
status OK()
body(file("response.json"))
headers {
contentType(applicationJson())
}
}
}
You can create two files 'request.json' and 'response.json' in the same directory. These JSON files include matching the request and response of the service you are looking to mock.
After this, run the Gradle test task — this will automatically generate the 'ContractVerfierTest' class and run your test (provided the endpoint you are testing is implemented).
Now, create a base verifier abstract test class. This is required for your 'spring-cloud-contract' plugin to generate the automated test implementation class. Create under the base package you mentioned in your build.gradle above.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public abstract class ContractVerifierBase {
@Autowired
private WebApplicationContext context;
@Before
public void setUp() throws Exception {
RestAssuredMockMvc.webAppContextSetup(context);
}
}
Now run Gradle -> VerificationVerification:: Check the task to ensure everything works properly. If your task successful, you will see the stubs jar created in the build/libs directory of your project.
Run your Gradle --> PublishStubPublicationtoMavenLocal. This will deploy your jars to the local maven repository (.m2 folder)You have now completed your first major step. Take a break!
Consumer Setup
Now, to consume the stub(mock) of Service B from Service A, we will use the Stub runner.
As always, add a dependency to your build.gradle of Service A.
testCompile("org.springframework.cloud:
"spring-cloud-starter-contract-stub-runner:2.0.1.RELEASE")
Create your test method in Service A which invokes your Service B Method and Run as Junit Test.
@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceBContractVerifierTest {
private String PAC_RESPONSE = "{\"traceId\":null,\"dbLatency\":0,\"transactionStatus\":null,\"transactionCategory\":null,\"iXMLResponse\":null,\"status\":false}";
private String ENDPOINT_URL = "http://localhost:8081/testservice";
@Autowired
RestTemplate restTemplate = new RestTemplate();
@Test
public void verifyServiceBTestEndpoint(){
ServiceBRequestBO request = new ServiceBRequestBO ();
// Given:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("X-xxx-traceId", request.getTraceId());
HttpEntity<String> entity = new HttpEntity(request, headers);
//When:
ResponseEntity<String> response = restTemplate.exchange(ENDPOINT_URL, HttpMethod.POST, entity, String.class);
// then:
Assertions.assertThat(response).isNotNull();
Assertions.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
Assertions.assertThat(response.getBody()).isEqualTo(PAC_RESPONSE);
}
This will make your test case failed. As you haven't configured your stub.
I/O error on POST request for "http://localhost:8081/testservice": Connection refused: connect; nested exception is java.net.ConnectException.
Configure your stub runner.
@AutoConfigureStubRunner(
ids = "com.mypackage.naveen.serviceB:serviceB:+:stubs:8081",
stubsMode = StubsMode.LOCAL)
Run your test again, it passes. HurrahHurrah!! Everything ends successfully.
Further Reading
Getting Started With Spring Cloud Gateway
Top 5 Courses to Learn Spring Cloud for Java Programmers in 2019
Opinions expressed by DZone contributors are their own.
Trending
-
Exploratory Testing Tutorial: A Comprehensive Guide With Examples and Best Practices
-
Cypress Tutorial: A Comprehensive Guide With Examples and Best Practices
-
What Is Istio Service Mesh?
-
Why You Should Consider Using React Router V6: An Overview of Changes
Comments