This is the third blog in the Consumer driven contract tests blog series. I introduced the concept in the first blog. Second blog covers writing contract testing using Pact for synchronous communication. In this blog, let’s cover how to write contract test when communication medium is message based.

In our example loan gateway emits the loan creation event. Loan fulfilment service listens to it and does further processing. In case of Http based communication, we have seen that Pact framework runs mock Http server. Message based communication differs from Http in way that there is no single standard way of communication. It can be established using various tools like Kafka, RabbitMQ, ActiveMQ etc. Pact may not want to couple itself with these tools, and hence it does not launch any of them while running the tests, rather it just enables us to make sure the event consumer and event producers adhere to the exact same schema. Ultimately that’s what we want! Let’s jump to the code.

Consumer test

Let’s start with the consumer test. In our example the listener in the loan fulfilment service is the consumer of the event emitted by loan gateway. Below are the steps to generate consumer tests and the contract.

  1. As usual, let's start with a spring boot test. As the LoanFulfilmentConsumer class is the consumer in this case, we will write a test for it. Let’s define the pact method first this time. We need to build MessagePact instead of RequestResponsePact. Below is the method.

    The method is self-explanatory. We describe basically what message contains. Let’s break it down.

    @Pact(consumer ="loan_fulfilment_service", provider = "loan_gateway")Provider and consumer names, these will be published along with the contract
    expectsToReceive("Loan creation event")This is the description of the interaction, the provider needs to provide a sample event from a method annotated with this description
    withMetadata(mapOf("traceId" to "1"))This is optional and to denote whether the message is going to contain a metadata or not
    withContent(...)Message body

  2. The test method is supposed to receive messages. And we need to make sure that the message adheres to the message object.

  3. We need to explicitly specify that the provider is going to be asynchronous. We can specify that using pactTestFor annotation. Below is the entire test.

    Running this test generates the contract in the target/pacts folder.

Provider test

  1. On the provider side we need to specify sample event which is adheres to the schema provided by the consumer.
  2. Let’s start with the spring boot test and add the pact test as below. We set up the context with target as AmpqTestTarget.
  3. Pact framework does not know from where to pick up the pact and which interactions to test from those pact files. Let’s supply that information via annotations.

  4. Run the test. Test fails with an exception No annotated methods were found for interaction 'Loan creation event'. You need to provide a method annotated with @PactVerifyProvider("Loan creation event") on the classpath that returns the message contents.
  5. Looking at the string ‘Loan creation event’ makes us realise that we have mentioned this string in the expectsToReceive clause. Let’s recall the purpose. The provider needs to provide a sample event from a method annotated with this description. Let’s add a method to provide the sample event.

    Below is the entire test.

  6. Run the test. Now it will pass and will emit the output as below.

    1
    2
    3
    4
    5
    6
    
    Verifying a pact between loan_fulfilment_service and loan_gateway
      [Using Directory target/pacts]
      Loan creation event
        generates a message which
          has a matching body (OK)
          has matching metadata (OK)

Congratulations! Now you know how to write contract test for services communicating asynchronous. You can find entire code on github. In the next blog we will focus a concept Pact broker.