Integration Testing using Spring Cloud Contracts and Stubs

Integration Test & Spring Cloud Contract:-

Now that all layers are implemented and tested in isolation let us perform the integration test.

Integration Test: -
• So far we have tested each layer independently .We can perform at the controller layer an integration test which tests the integration of all layers and its not tested on an actual server
• Its similar to Controller layer unit tests except that here nothing is mocked and end-to-end scenarios will be executed

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class SSNControllerTests2 {
@Autowired
MockMvc mockmvc;
@Autowired
protected WebApplicationContext context;
@Test
public void test1() throws Exception
{
//write the test case using mockmvc
}

}

Here Mockmvc object will be configured with applicationcontext and all beans managed by Application context will be available.

Integration Test: Scenario 2
• But our use case is not simple, our CustomerManagement microservice invokes ExtService microservice to complete its functionality of adding a customer with valid SSN#.
• In such a context how do we test it?



When trying to test an application that invokes extrernal services for that we could do any one of the
below things:
1. Deploy the entire set of microservices and execute end to end tests.
or
2. Mock the needed microservices in integration/unit tests

1. Deploy all microservices and perform end to end tests: -
Advantages
• simulates production
• tests real communication between services

Disadvantages
• To test one microservice we would have to deploy all microservices, a couple of databases etc.
• the environment where the tests would be conducted would be locked for a single suite of tests
(i.e., nobody else would be able to run the tests in the meantime).
• long to run
• very late feedback
• extremely hard to debug

2. Mock other microservices in unit / integration tests: -
Advantages
• very fast feedback
• no infrastructure requirements

Disadvantages
• The implementor of the service creates stubs thus they might have nothing to do with the reality
• You can go to production with passing tests and failing production

Solution?
Use Spring Cloud Contract Verifier with Stub Runner
• Why Spring Cloud Contract Verifier with Stub Runner?
– To resolve the issues just discussed, Spring Cloud Contract Verifier were generated.
– Their idea is to provide with actual fast feedback, without the need for setting up the complete
world of microservices.
 Ensure that Messaging/HTTP stubs (used when emerging the client) are doing the exact of what real
server-side execution will do.
 Continue to promote Microservices architectural style and acceptance test driven development
method
 to provide a technique to announce variations in contracts that are directly visible on both sides of the
communication
 to produce boilerplate test code used on the server side.

Microservices interactions: -
• We are ready with the isolated testcase of our Customer Microservice, but while performing the
integration test where our implemented service layer calls the ExtService api we do not want to mock it
, we want a simulated server call for which the Spring Cloud contract will help us.



Let us see how the stubs can be generated and distributed for our ExtService api which can be used
while testing CustomerService.

Generating Stubs:-

We are ready with the isolated testcase of our Customer Microservice ,but while performing the
integration test where our implemented service layer calls the ExtService api we do not need to use a
mock object, we want a simulated server call for which the Spring Cloud contract will help us.
• Let us see how the stubs can be generated and distributed for our ExtService api which can be used
while testing CustomerService.
• Here is what the ExtService exposes

Steps for generating Stubs: -

1. Add the SCC dependency and plugin under the correct section in POM.xml
2. Add SCC plugin under the correct section in POM.xml and also mention the base test class which will have the base setup for the test case class that will be generated

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-verifier</artifactId>
<version>2.1.1.RELEASE</version>
<scope>test</scope>
</dependency>

<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<extensions>true</extensions>
<configuration>
<baseClassForTests>
com.extservice.controller.SSNControllerTests
</baseClassForTests>
</configuration>
</plugin>

3 .Create the contract (Contract defines what the endpoint is supposed to do) using either a
Groovy/YAML DSL.
• We will be using YAML DSL

/ssnapi--> Valid data
       --> Invalid data --> Invalid ssn details
                        --> Missing data in request

• As per the above requirement we need 3 contracts (1 positive and 2 Negative test cases)

Yaml File -Contract 1 (Invalid details) : -

• Ssndetails_invalid.yml
          name: Ssndetails_invalid
request:
method: GET
url: /ssnapi
queryParameters:
ssnId: 123456789010
firstName: Roberta
lastName: Smith
response:
headers: # (10)
Content-Type: application/json;charset=UTF-8
status: 400
bodyFromFile: Ssndetailsmissingresp.json

Ssndetailsmissingresp.json
"errors": [
          {
"code": "input.parameter.missing",
"message": "ssnId input parameter is missing in request"
}
        ]

Yaml File :Contract 2 ( valid details): -
• Ssndetails_valid.yml
          name: Ssndetails_valid
request:
method: GET
url: /ssnapi
queryParameters:
ssnId: 123456789010
firstName: Robert
lastName: Smith
response:
headers: # (10)
Content-Type: application/json;charset=UTF-8
status: 200
bodyFromFile: Ssndetailsvalidresp.txt

Ssndetailsvalidresp.txt
true

Yaml file :contract 3 ( missing data in request): -
• Ssndetails_missing.yml
          name: Ssndetails_missing
request:
method: GET
url: /ssnapi
response:
headers: # (10)
Content-Type: application/json;charset=UTF-8
status: 400
bodyFromFile: Ssndetailsmissingresp.json

Ssndetailsmissingresp.json
{
"errors": [
{
"code": "input.parameter.missing",
"message": "ssnId input parameter is missing in request"
}
]
}

 

 4. All the contracts have to be base lined in the below folder of Spring Boot Project
       /src/test/resources/contracts



• Write the base test class:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@DirtiesContext
public class SSNControllerTests {
@Test
public void contextLoads() {
}
@Autowired
protected WebApplicationContext context;
@Before
public void setup() {
DefaultMockMvcBuilder standaloneMockMvcBuilder =
MockMvcBuilders.webAppContextSetup(context);
RestAssuredMockMvc.standaloneSetup(standaloneMockMvcBuilder);
}

Note: This base test class will be used for auto-generating the tests .This class was what we mentioned in the projects
POM.xml . Also the auto-generated tests and contains the complete setup needed to run them (ex: messaging test setup or
RestAssuredMockMvc controller setup).

Annotations used in base test class: -


5.
– Run mv clean install -DskipTest=true ,this will automatically creates tests that validate the
application compliance with the contracts that are added
– By default, the created tests are below org.springframework.cloud.contract.verifier.tests.


Note: We had added a base test class for auto-generated tests to the project in POM.xml. This class will
extended by all the auto-generated tests and contains entire setup/ essential information required to run
them (for ex, messaging test setup or RestAssuredMockMvc controller setup).

The generated tests may differ, depending on which framework and test type you have setup in your plugin.


The generated stub will be the one to be shared. Stubs can be generated by the provider end or a consumer end.
extservicefinalv2-v2.jar

provider contract testing (the producer has no information of how clients use their API). The stubs are
uploaded to a different repository (they are not uploaded to Nexus or Artifactory).

consumer-driven contracts : Holds the complete contract descriptions for each producer. The
producer produces tests from the locally stored JAR and writes the missing implementation to make the
tests pass.
We will follow the provider contract testing and place it in the repository either locally/globally.

Using the stub to perform the integration test of the consumer (Customer Microservice): -

• Spring Cloud Contract Stub Runner is added so that in the integration tests we develop a running messaging route or WireMock instance that simulates the actual service.

• In the test case ,get the existing producer service stubs from a remote repository.
• To do so, pass the stub artifact IDs and artifact repository URL as Spring Cloud Contract Stub Runner properties, as shown below

stubrunner:
ids: 'com.javahubpoint:ExtService:+:stubs:8083'
[repositoryRoot: https://repo.spring.io/libs-snapshot]

• Id is a mixture of the groupId:artifactid:+:stubs:<<port# where the stub should run>>
• The version need not be mentioned for the jar as the latest will be picked up from the repository as we have given “+” between the artifactid and “stubs” keyword
• This property should be set in the @AutoConfigureStubRunner annotation which will be applied on the integration test class.

Writing the Integration TestCase class: -

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
@AutoConfigureJsonTesters
@DirtiesContext
@AutoConfigureStubRunner(stubsMode = StubsMode.LOCAL, ids = "com.javahubpoint:extservice:+:Stubs:8083")
public class TestCustomerController {
@Autowired
protected WebApplicationContext context;
@Test
public void contextLoads() {
}
@Before
public void setup() {
DefaultMockMvcBuilder DefaultMockMvcBuilder = MockMvcBuilders.webAppContextSetup(context);
RestAssuredMockMvc.standaloneSetup(standaloneMockMvcBuilder);
}
}

Instead of writing the testcases let us make use of SpringCloudContractVerifier which will autogenerate the test case class for us if we provide the contract files and the base test class.
Let us look at the base test class which will be given in the POM.xml of Customer Microservice Boot project as well

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
@AutoConfigureJsonTesters
@DirtiesContext
@AutoConfigureStubRunner(stubsMode = StubsMode.LOCAL, ids = "com.javahubpoint:extservice:+:Stubs:8083")
public class TestCustomerController {
@Autowired
protected WebApplicationContext context;
@Test
public void contextLoads() {
}
@Before
public void setup() {
DefaultMockMvcBuilder DefaultMockMvcBuilder = MockMvcBuilders.webAppContextSetup(context);
RestAssuredMockMvc.standaloneSetup(standaloneMockMvcBuilder);
}
}


Define the contracts for Customer Microservice: -


• You will notice that the ContractVerifierTest will be generated which will work with real Service Layer and Repository layer and wherever there is a need to call the external api it will make use of the
stubrunner.
• In case any test fails. Go back and refractor the code so that test cases pass.
• Keep repeating this until all test case pass.