{{announcement.body}}
{{announcement.title}}

Testing With Spring Cloud Contract

DZone 's Guide to

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.

· Performance Zone ·
Free Resource

clouds

Learn how to test with Spring Cloud.

With 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".

consumer to provider

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).

failed test

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.

ConsumerContract

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

Microservices Architecture: Introduction to Spring Cloud

Topics:
functional test, microservices testing, performance, spring boot, spring cloud, spring cloud contract, testing

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}