Home » Java » Enterprise Java » Consumer Driven Testing with Pact & Spring Boot

About Brian Du Preez

Brian Du Preez

Consumer Driven Testing with Pact & Spring Boot

Recently a colleague of mine stumbled across Pact.io,  Our current application had grown to over 50 services and we we’re starting to have some integration test failures and a brittle dev / acceptance test environment. So we decided to have a look at ways to try help with this.

I started out by reading: https://docs.pact.io/faq/convinceme.html

Then watching: https://www.youtube.com/watch?v=-6x6XBDf9sQ&feature=youtu.be

Those 2 resources convinced me to give it a shot.

So I set out and created a quick set of Spring boot apps, the GitHub repo here, to test out the concepts and get everything working.

To highlight some important bits from the demo.

Consumer:

As Pact is a consumer driven test framework. This is where you define a unit test, that test mocks the http server response and you assert against that.

Once the test is successful it creates a pact json file in the /pacts directory.

public class TestProvider {
    @Rule
    public PactProviderRule provider = new PactProviderRule("test_provider", "localhost", 8081, this);

    @Pact(state = "default", provider = "test_provider", consumer = "test_consumer")
    public PactFragment createFragment(PactDslWithProvider builder) {
        Map<String, String> headers = new HashMap<>();
        headers.put("content-type", "application/json");

        return builder
                .given("default")
                .uponReceiving("Test User Service")
                .path("/user/1")
                .method("GET")
                .willRespondWith()
                .status(200)
                .headers(headers)
                .body("{" +
                        "  \"userName\": \"Bob\",\n" +
                        "  \"userId\": \"1\",\n" +
                        "  \"firstName\": null,\n" +
                        "  \"lastName\": null,\n" +
                        "  \"email\": null,\n" +
                        "  \"groups\": null\n" +
                        "}")
                .toFragment();
    }

    @Test
    @PactVerification("test_provider")
    public void runTest() throws IOException {
        final RestTemplate call = new RestTemplate();
         final User expectedResponse = new User();
        expectedResponse.setUserName("Bob");
        expectedResponse.setUserId("1");
        final User forEntity = call.getForObject(provider.getConfig().url() + "/user/1", User.class);
        assertThat(forEntity, sameBeanAs(expectedResponse));

    }
}

So after the “mock” test is run and the pact file has been created. You need to include a maven plugin …pact… that is then used to publish the content of the pacts/ folder to the pact broker… which is defined in the pom as below.

<dependencies>
	<!-- https://mvnrepository.com/artifact/au.com.dius/pact-jvm-consumer-junit_2.11 -->
	<dependency>
		<groupId>au.com.dius</groupId>
		<artifactId>pact-jvm-consumer-junit_2.11</artifactId>
		<version>3.3.6</version>
		<scope>test</scope>
	</dependency>
</dependencies>

<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-surefire-plugin</artifactId>
			<version>2.18</version>
			<configuration>
				<systemPropertyVariables>
					<pact.rootDir>pacts</pact.rootDir>
					<buildDirectory>${project.build.directory}</buildDirectory>
				</systemPropertyVariables>
			</configuration>
		</plugin>
		<plugin>
			<groupId>au.com.dius</groupId>
			<artifactId>pact-jvm-provider-maven_2.11</artifactId>
			<version>3.3.4</version>
			<configuration>
				<pactDirectory>pacts</pactDirectory>
				<pactBrokerUrl>http://localhost:80</pactBrokerUrl>
				<projectVersion>1.0.1</projectVersion>
			</configuration>
		</plugin>
	</plugins>
</build>

Producer:

This uses the JUnit integration from Pact.io to download the pacts from the broker and then run against an running service.

Since this already uses a @RunWith annotation, I could not use the spring boot runner. So to get around that as a before class step, I start the Spring boot application, the pacts then gets run against that running instance… and the boot application gets stopped again after the tests. Depending on your use case I guess it would also be an option to do this with @Before so you get a new service instance started before each pack, but that would slow down the execution tremendously.

The @State annotation, allows for clients to define a specific state, which the producer can the use to setup additional data / conditions required for the test to run.

Once the pacts have executed against the service there are reports generated in the target folder.

@RunWith(PactRunner.class)
@Provider("test_provider" )
@PactBroker(host = "localhost", port = "80")
@VerificationReports({"console", "markdown"})
public class TestPacts {
    private static ConfigurableApplicationContext application;
    
    @TestTarget
    public final Target target = new HttpTarget(8080);
    
    @BeforeClass
    public static void startSpring(){
        application = SpringApplication.run(ProviderServiceApplication.class);
    }

    @State("default")
    public void toDefaultState() {
        System.out.println("Now service in default state");
    }

    @State("extra")
    public void toExtraState() {
        System.out.println("Now service in extra state");
    }

    @AfterClass
    public static void kill(){
        application.stop();
    }
}

Setting up the Pact Broker

1. Grab the public images from Docker Hub.

docker pull dius/pact_broker
docker pull postgres

2. Then setup the Postgres DB

docker run --name pactbroker-db -e POSTGRES_PASSWORD=ThePostgresPassword -e POSTGRES_USER=admin -d postgres
docker run -it --link pactbroker-db:postgres --rm postgres psql -h postgres -U admin
CREATE USER pactbrokeruser WITH PASSWORD 'TheUserPassword';
CREATE DATABASE pactbroker WITH OWNER pactbrokeruser;
GRANT ALL PRIVILEGES ON DATABASE pactbroker TO pactbrokeruser;

3. Once the DB is up, run the actual Broker:

docker run --name pactbroker --link pactbroker-db:postgres -e PACT_BROKER_DATABASE_USERNAME=pactbrokeruser -e PACT_BROKER_DATABASE_PASSWORD=TheUserPassword -e PACT_BROKER_DATABASE_HOST=postgres -e PACT_BROKER_DATABASE_NAME=pactbroker -d -p 80:80 dius/pact_broker

Extra References:

Get the example project

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

 

1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design

 

and many more ....

 

Receive Java & Developer job alerts in your Area from our partners over at ZipRecruiter

 

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Want to take your Java skills to the next level?

Grab our programming books for FREE!

Here are some of the eBooks you will get:

  • Spring Interview QnA
  • Multithreading & Concurrency QnA
  • JPA Minibook
  • JVM Troubleshooting Guide
  • Advanced Java
  • Java Interview QnA
  • Java Design Patterns