Link Search Menu Expand Document

Programmatic Approach

If you are building your application in a JVM language, you are in luck, because you can run programmatically the contract as a stub server or as tests.


Add jar dependency. Notice this is test only scope. There is no need to ship Specmatic jar with your production code.


Author a contract

Copy paste below text into a file with name “service.spec”. This, as you can see, uses the Gherkin syntax to describe a basic GET request.

Feature: Contract for the petstore service

Scenario: Should be able to get a pet by petId
  When GET /pets/(petid:number)
  Then status 200
  And response-body {petid: "(number)"}

There are some extra keywords that make it easier to define APIs.

  • GET and related URL
  • status
  • response-body
  • (number) - placeholder for number datatype

These keywords are documented in the contract syntax reference. TODO

Consumer - Leveraging Stub Server

Let us try building a Pet Store Consumer through Test First approach.

First add the following json to a stub file named expectation.json, in a directory named petstore_data:

  "http-request": {
    "method": "GET",
    "path": "/pets/10"
  "http-response": {
    "status": 200,
    "body": {
      "name": "Archie",
      "type": "dog",
      "status": "available",
      "id": 10

Then add the test below to your codebase.

package com.petstore.test;

import com.petstore.demo.PetStoreConsumer;
import com.petstore.demo.model.Pet;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import run.spec.stub.ContractStub;

import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static run.spec.stub.API.*;

public class PetStoreConsumerTest {
    private static ContractStub petStoreStub;

    public static void setUP() {
        // Path to the contract
        List<String> contracts = new ArrayList<String>();

        // Path to stub data directory
        List<String> stubDataDir = new ArrayList<String>();

        //Start the stub server
        petStoreStub = createStubFromContracts(contracts, stubDataDir, "localhost", 9000);

    public void shouldGetPetByPetId() {
        PetStoreConsumer petStoreConsumer = new PetStoreConsumer("http://localhost:9000");
        Pet archie = petStoreConsumer.getPet(10);

        assertEquals(10, archie.getId());
        assertEquals("Archie", archie.getName());

    public static void tearDown() throws IOException {

Let us take a closer look at the above test.

  • The objective of this test is to help us build a PetStoreConsumer (API Client) class.
  • The setUP and tearDown methods are responsible for starting a Specmatic Stub server (based on the service.spec) and stopping it respectively.
  • In the setUP section, we pass the expectation json file to the stub. This tells it to expect a call to /pets/10, to which it must return the given pet info.
  • The assert section verifies that PetStoreConsumer is able to translate the response to Pet object

At this point you will see compilation errors because we do not have PetStoreConsumer and Pet classes. Let us define those.

package com.petstore.demo;

import com.petstore.demo.model.Pet;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;


public class PetStoreConsumer {
    private final String petStoreUrl;

    public PetStoreConsumer(String petStoreUrl) {
        this.petStoreUrl = petStoreUrl;

    public Pet getPet(int id) {
        RestTemplate restTemplate = new RestTemplate();
        URI uri = URI.create(petStoreUrl + "/pets/" + id);
        ResponseEntity<Pet> response = restTemplate.getForEntity(uri, Pet.class);
        return response.getBody();

Now we can run the PetStoreConsumerTest. This test is fast and also does not require the real backend api to be running.

Since this test runs the contract as a stub we can be sure that when there are changes in the contract, this test will fail because of mismatch errors in stub setup section.

This is important because it helps us keep the Consumer in line with the Provider.

Provider - Running Contract as a test

Add JUnit Jar dependency. This lets you run the contract as a JUnit 5 test.


Specmatic leverages testing Frameworks to let you run contract as a test. At the moment JUnit 5 is supported. Each Scenario in translated to a junit test so that you get IDE support to run your contract.

Add below test to your Provider.

import run.spec.test.specJUnitSupport;


public class PetStoreContractTest extends SpecmaticJUnitSupport {
    private static ConfigurableApplicationContext context;

    public static void setUp() {
        File contract = new File("contract/service.spec");
        System.setProperty("contractPaths", contract.getAbsolutePath());
        System.setProperty("host", "localhost");
        System.setProperty("port", "8080");

        context =;

    public static void tearDown() {

A closer look at above test.

  • PetStoreContractTest extends SpecmaticJUnitSupport. SpecmaticJUnitSupport leverages JUnit5 Dynamic Tests to translate scenarios in the contract to tests.
  • The setUp method passes the location of contract file, host port etc. to SpecmaticJUnitSupport through System Properties
  • Optional - You can start and stop the application in setUp and tearDown. In this example we are starting a Springboot application.
  • Note - Please do not add any other unit test to the above class. The above test is only supposed to have setUp and optionally tearDown methods.

To run this test, right click (or use JUnit shortcuts) and run it just like a unit test.

Following the test-first style approach, we haven’t written a pet store API yet, so the contract test will fail. We now need to add a pet store API that passes the contract tests. We leave this as an exercise for the reader.