In previous posts, I talked about the characteristics of contracts and mentioned some of its advantages and disadvantages. Now it is time for a deep-dive into the technical (and funniest) side to see how you can develop contracts for your API and produce Rest-Assured tests and Wiremock stubs in just a few steps.

Writing contracts

The only thing we need to start writing contracts is the OpenApi (or Swagger) specification and – to make it quick – I will use the same API I defined on my previous post, more precisely the following endpoints:

  • GET /projects
  • GET /projects/{project-id}

Let’s start with the first endpoint. What do we expect our API to do if we perform that request? The happy path would be a response with http status code 200 and a list of projects in the body, so our contract will be something like:

  • Request:
    • Http method: GET
    • url /tasklist-management/api/projects
  • Response:
    • Http status: 200
    • body: {project-1, project-2}

The next step is to translate it into code. I will use Groovy because I think the code will be more readable and I am used to write tests with it, but it also works with Java directly. 

def contracts = []

contracts << Contract.make {
    name("get projects should return 200 code and list of projects")
    description('''
given:
when:
    get /projects is called
then:
    should answer with http 200 and the list of projects
''')
    request {
        method("GET")
        urlPath "/tasklist-management/api/projects"
    }
    response {
        status(200)
        headers {
            contentType(applicationJson())
        }
        body([
                [
                        projectId  : 1,
                        title      : 'My awesome project 1',
                        description: 'This is the first project created with the best api',
                ],
                [
                        projectId  : 2,
                        title      : 'My second project',
                        description: 'Another awesome project',
                ]
        ]
        )
    }
}

As you can see, the different parts I mentioned above can be identified very easily in the contract. This is a best case example, but you can define more contracts to cover the edge cases, like searching for a project which does not exist and delivering a 404 instead of a 200 and so on.

I defined another example for the second endpoint, you can find it in the Github repository.

Configuring the plugin

Our contracts have already been defined, so now it is time for the Spring Cloud Contract Verifier plugin to come into game. Generally, we will feed the plugin with our contracts and it will produce the tests and mock stubs for us, but for that there are some parameters which need to be configured.

First of all, we need to add the dependency in our pom.xml and configure the plugin. There are more parameters to configure the plugin but I will show you the basic ones. We will use Spock as framework for our Rest-Assured tests, so we need to add some extra dependencies to get them running. Do not worry, you will find the link to my Github repository at the end of the post ;).

Next step is to define the execution part, which in our case consists in generating the tests and WireMock stubs before the integration tests phase. For that, the following goals need to be defined:

  • convert: Basically converts the contracts into stubs mappings allowing to generate a ‘stubs-jar’ that we will need later
  • generateStubs: Creates a jar file with the previously converted stubs. Requires convert to be executed first.
  • generateTests: Picks the contracts from the provided directory and generates the Rest-Assured tests using the framework that we defined in the plugin configuration

As a result, this is how the plugin should look like:

<!-- Spring Cloud Contract Maven plugin, generates Rest-Assured tests and Wiremock-stubs from contracts -->
<plugin>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
	<version>${spring-cloud-contract.version}</version>
	<extensions>true</extensions>
	<configuration>
		<packageWithBaseClasses>${project-base-package}</packageWithBaseClasses>
		<contractsDirectory>src/test-it/resources/contracts</contractsDirectory>
		<basePackageForTests>${project-base-package}.tests</basePackageForTests>
		<testMode>MOCKMVC</testMode>
		<testFramework>SPOCK</testFramework>
		<classifier>stubs</classifier>
		<nameSuffixForTests>IntegrationTest</nameSuffixForTests>
	</configuration>
	<executions>
		<execution>
			<id>contract-based-tests</id>
			<phase>pre-integration-test</phase>
			<goals>
				<goal>convert</goal>
				<goal>generateStubs</goal>
				<goal>generateTests</goal>
			</goals>
		</execution>
	</executions>
</plugin>

Let’s run it

At this point we have our OpenApi specification, some contracts which define the behavior of the API and our plugin ready to work. Let’s see what we can do! For that, just run the following maven command:

After that, this is what you should see:

It looks like groovy could not compile the output, so let’s check what has been generated and try to fix the problem. The generated tests should be available at /target/generated-test-sources/contracts/

If we open the generated tests, we will see some compilation errors:

As you can see, the test has been successfully generated, but unfortunately the ContractVerifierBase class is missing. Well, we need to program it so that our tests can find it. This class will contain the base configuration, so we will define things like our tests run with the Spock framework (Specification) and the web environment (MockMvc):

@SpringBootTest(
  webEnvironment = SpringBootTest.WebEnvironment.MOCK, 
  classes = Application.class
)
@AutoConfigureMessageVerifier
@AutoConfigureMockMvc
class ContractVerifierBase extends Specification {

    @Autowired
    private MockMvc mockMvc;

    def setup() {
        RestAssuredMockMvc.mockMvc(mockMvc)
    }
}

If we execute the generator once again, we will see that the plugin finishes successfully and we get the stubs and integration tests generated in the ContractVerifierIntegrationTest.groovy class.

def validate_get_project_by_id_should_return_200_code_and_the_project_information() throws Exception {
	given:
		MockMvcRequestSpecification request = given()


	when:
		ResponseOptions response = given().spec(request)
				.get("/tasklist-management/api/projects/1")

	then:
		response.statusCode() == 200
		response.header("Content-Type") ==~ java.util.regex.Pattern.compile('application/json.*')

	and:
		DocumentContext parsedJson = JsonPath.parse(response.body.asString())
		assertThatJson(parsedJson).field("['projectId']").matches("-?(\\d+)")
		assertThatJson(parsedJson).field("['title']").matches("[\\S\\s]+")
		assertThatJson(parsedJson).field("['description']").matches("[\\S\\s]+")
}

Since we have not implemented the any of the endpoints yet, if we execute the tests at this point they will fail. This behavior follows the TDD (Test Driven Development) principle, as we first develop the tests and afterwards the application to make the tests run successfully.

Now let’s focus on the second part: the stubs. After running the plugin, we will find a new jar inside the target folder with the classifier *-stubs. This jar contains only the WireMock stubs and we could provide them to our client or consumer, so that they can start with the development of the frontend page or mobile app using those Mocks as backend while we start with the real development of the API. For that, we can develop a WireMock server which will load the stubs from the class path and run as a completely standalone application that we could deploy to a development or integration environment.

Steps to run the Mocks:

  • Install the stubs in your local Maven repository (you can use Nexus or Artifactory as well).
  • Download the standalone WireMock server from my Github repository and import the stubs dependency in the pom. Keep in mind that we only need the stubs and ignore any other file in the jar.
<!-- Generated Wiremock stubs -->
<dependency>
  <groupId>com.binary.mindset</groupId>
  <artifactId>api-contracts</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <classifier>stubs</classifier>
  <exclusions>
    <exclusion>
      <groupId>*</groupId>
      <artifactId>*</artifactId>
    </exclusion>
  </exclusions>
</dependency>

  • Compile and start the application. You can pass the port where you want to run it as argument, if not, a dynamic port will be chosen by default.
  • Test it and enjoy! Since I have developed only two contracts, the WireMock will respond only to this endpoints and always with the same response I configured, that means, the WireMock will have a “static” behavior.

We have obtained in a very easy way a Mock whose behavior is 100% how we developed it in the contract, but this will not be optimal in most of the cases, because we may give some flexibility to the consumers that will use the mocks.

Let’s improve the Mock

The Mocks work as we tell them and, in this case, as we program the contracts, so if we want to improve the mocks, we will need to modify something in the contracts. The good news is that the Spring Cloud Contract makes it very easy by using the consumer-producer pair.

This way we could say to our contract, for whatever project-id you get, give me this response, and if you get project-id=99 (like before) then give me another response. It is very helpful and allows us to provide a Mock that always provides a response whatever request it receives, and for very specific cases (edge cases) changes its behavior.

Let’s see how such kind of contract is written:

def contracts = []

contracts << Contract.make {
    priority(2)
    name("get project by id should return 200 code and the project information")
    description('''
given:
when:
    get /project/{project-id} is called
then:
    should answer with http 200 and the project information
''')
    request {
        method("GET")
        urlPath $(consumer(regex("/tasklist-management/api/projects/$PATTERN_ANY_STRING_NUMBER")),
                producer("/tasklist-management/api/projects/1"))
    }
    response {
        status(200)
        headers {
            contentType(applicationJson())
        }
        body([
                projectId  : $(consumer(1), producer(anyInteger())),
                title      : $(consumer('My awesome project 1'), producer(anyNonEmptyString())),
                description: $(consumer('This is the first project created with the best api'), producer(anyNonEmptyString()))
        ]
        )
    }
}

It is very important to say that we added a new parameter, which is the priority (line 10). When we develop this kind of contracts, we need to provide them a lower priority than the specific contracts, otherwise they will always react to any input and the behavior of the specific contracts will be ignored.

To test it, repeat the same steps as before and run the WireMock server. When you perform a request now you will obtain the expected result:

And there you go, now our Mocked-API will answer to any {project-id}, but now for the very specific case that we set, 99, the response will be different.

Summary

As we have seen, contracts are a very powerful tool that we can use on a very early stage of development, even if we have not started with the implementation yet. Just by using the OpenApi spec and contracts we can provide a more than valid Mocked-Api whose behavior will be the same than the real API (it is defined in the contract). This will allow to parallelize the development on the consumer side as well as the provider side.

Furthermore, we obtained Rest-Assured tests that are responsible for proving that our API is behaving as we defined in the contract, increasing the test coverage tremendously this way. Automating those tests via CI would be a very good decision that will improve the quality of your API.

The complete project is available on Github and you can download it at the link below.

Enjoy it!