Api Micro-service (HTTP) Contract Test

Reading Time: 4 minutes

A pact is a code-first tool for testing HTTP and message integrations using contract tests. It’s will help you to have a write HTTP Contract test.

Contract-driven testing

  • Have you ever traveled to a country where people don’t speak your native language? When two people don’t speak the same language, it’s hard for them to accomplish anything together, So for resolve that issue we will use HTTP Contract test.
  • The same is true of microservices. If two microservices are to work together, they must “speak” the same language. The language that is share between two microservices is define in an API. One microservice, the consumer, makes a request to a second microservice, the provider, via the API. This event is also know an interaction. For the interaction to succeed, the provider must understand the request from the consumer and return a response in a form that the consumer expects.

  • Consider an example of an airline reservation system where you are the passenger. You make a reservation through flight and the user interface calls the Reservation microservice. You also want to select seats, so the Reservation service makes a request to the Seat Assignment microservice to provide a list of the available seats then You select a seat and the Reservation service again calls the Seat Assignment microservice to assign the seat to you.
  • The API-driven interactions define the terms of a contract, or pact, between the microservices. Automated tests of the consumer can be use to generate the contract. After the contract interactions are define, automated tests will be write to validate both the consumer and provider by using the stored contracts.

Consumer-Driven Contract Testing

  • When you can’t test the real interaction between two microservices, you must test it in a simulated way. In the airline example, you can’t assign people to seats on real flights.
  • To solve this problem, developers use a simulated version of the provider service in a test environment and run a set of automated test cases to test the consumer. The simulated service receives a request and sends a minimal expected response that can be processed by the consumer. The simulated provider must process every interaction that the consumer can request.

  • To complete the testing of the contract, you must test the provider by using a simulated version of the consumer. The requests are sent to the provider, who sends a response. The provider response will compare to the expected response.

JSON Maven dependency

 <!-- For start the service -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- it use for mock api HTTP testing -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- it use for message-pact-provider -->
        <dependency>
            <groupId>au.com.dius</groupId>
            <artifactId>pact-jvm-consumer-junit</artifactId>
            <version>4.0.10</version>
        </dependency>

PACT

  • Firstly we saved the postgres and pact-broker images in yml file. So use of that file we can run the pact-broker service.
version: "3.2"
services:
  postgres:
    image: postgres
    healthcheck:
      test: psql postgres --command "select 1" -U postgres
    ports:
      - "5432:5432"
    volumes:
      - postgres-volume:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: postgres

  pact-broker:
    image: pactfoundation/pact-broker:2.74.1.0
    ports:
      - "9090:9090"
    depends_on:
      - postgres
    environment:
      PACT_BROKER_PORT: '9090'
      PACT_BROKER_DATABASE_URL: "postgres://postgres:password@postgres/postgres"
      PACT_BROKER_LOG_LEVEL: INFO
      PACT_BROKER_SQL_LOG_LEVEL: DEBUG


volumes:
  postgres-volume:

  • Use the below command to run the docker-compose file.
docker-compose up
  • Now we are writing the code for a contract through microservice.
@Rule
public PactProviderRule mockProvider = new PactProviderRule("inventory_provider","localhost", 8080, this);
private RestTemplate restTemplate=new RestTemplate();


@Pact(provider = "inventory_provider", consumer = "inventory_consumer")
public RequestResponsePact createPact(PactDslWithProvider builder) {
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", MediaType.APPLICATION_JSON_VALUE);


PactDslJsonBody bodyResponse = new PactDslJsonBody()
.stringValue("productName", "TV")
.stringType("locationName", "CHENNAI")
.integerType("quantity", 100);

return builder
.given("create inventory").uponReceiving("a request to save inventory")
.path("/api/inventory")
.body(bodyResponse)
.headers(headers)
.method(RequestMethod.POST.name())
.willRespondWith()
.headers(headers)
.status(200).body(bodyResponse).toPact();
}
  • For verification of generated PACT, we use @PactVerification annontation.
@Test
	@PactVerification
	public void testCreateInventoryConsumer() throws IOException {

		Inventory inventory=new Inventory("TV", "CHENNAI", 100);
		HttpHeaders headers=new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		HttpEntity<Object> request=new HttpEntity<Object>(inventory, headers);
		System.out.println("MOCK provider URL"+mockProvider.getUrl());
		ResponseEntity<String> responseEntity=restTemplate.postForEntity(mockProvider.getUrl()+"/api/inventory", request, String.class);
		assertEquals("TV", JsonPath.read(responseEntity.getBody(),"$.productName"));
		assertEquals("CHENNAI", JsonPath.read(responseEntity.getBody(),"$.locationName"));
		assertEquals((Integer)100, (Integer)JsonPath.read(responseEntity.getBody(),"$.quantity"));
	}
  • Here is the Inventory.java file where we save the variable and set the getter-setter and string values.
  • Add the @PactFolder(//location of generated pact) annotation in upper the class.
  • Add the pact-broker URL in the POM file.

Generated Pact

{
  "provider": {
    "name": "inventory_provider"
  },
  "consumer": {
    "name": "inventory_consumer"
  },
  "interactions": [
    {
      "description": "a request to save inventory",
      "request": {
        "method": "POST",
        "path": "/api/inventory",
        "headers": {
          "Content-Type": "application/json"
        },
        "body": {
          "locationName": "CHENNAI",
          "quantity": 100,
          "productName": "TV"
        },
        "matchingRules": {
          "body": {
            "$.locationName": {
              "matchers": [
                {
                  "match": "type"
                }
              ],
              "combine": "AND"
            },
            "$.quantity": {
              "matchers": [
                {
                  "match": "integer"
                }
              ],
              "combine": "AND"
            }
          }
        }
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": "application/json"
        },
        "body": {
          "locationName": "CHENNAI",
          "quantity": 100,
          "productName": "TV"
        },
        "matchingRules": {
          "body": {
            "$.locationName": {
              "matchers": [
                {
                  "match": "type"
                }
              ],
              "combine": "AND"
            },
            "$.quantity": {
              "matchers": [
                {
                  "match": "integer"
                }
              ],
              "combine": "AND"
            }
          }
        }
      },
      "providerStates": [
        {
          "name": "create inventory"
        }
      ]
    }
  ],
  "metadata": {
    "pactSpecification": {
      "version": "3.0.0"
    },
    "pact-jvm": {
      "version": "4.0.10"
    }
  }
}
  • Now publish the generated pact into Pact-broker.

Techhub Template Reference

https://github.com/knoldus/api-microservices-Contract-test.

Referenece

https://docs.pact.io/


Knoldus-blog-footer-image

Written by 

Prajjawal is a QA Consultant having experience of more than 1.6 year. He is familiar with core concepts of manual & automation testing using tools like Contract test, Selenium, and Postman Also having knowledge of Core Java, Python and Data Science. He is always eager to learn new and advanced concepts in order to improve himself. He likes to watch web series and play cricket.