Skip to content

pedrohrr/pact-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Simple example of pact validation

Build Status

An extremely simple example of how to implement pact validation between two microservices. Even though it is simple, it covers the complete flow of a pact, with working examples of both consumer and provider.

Consumer was build with:

Provider was build with:

Generating the pact file

The validation of a pact starts by creating the pact from the consumer perspective.

  • Adding dependencies
repositories {
    jcenter()
    mavenCentral()
}

dependencies {
    implementation("khttp:khttp:0.1.0")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10")
    implementation("com.fasterxml.jackson.core:jackson-databind:2.11.3")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.3")

    testImplementation("org.junit.jupiter:junit-jupiter:5.4.2")
    testImplementation("org.junit.jupiter:junit-jupiter-engine:5.5.2")
    testImplementation("org.assertj:assertj-core:3.9.1")
    testImplementation("au.com.dius.pact.consumer:junit5:4.1.0")
}
  • Specifying folder to generate pacts
tasks.withType<Test> {
    useJUnitPlatform()
    systemProperties["pact.rootDir"] = "$buildDir/../../pacts"
}
  • Creating pact test
package org.pedrohrr.pacts.consumer.clients

import au.com.dius.pact.consumer.MockServer
import au.com.dius.pact.consumer.dsl.PactDslJsonBody
import au.com.dius.pact.consumer.dsl.PactDslWithProvider
import au.com.dius.pact.consumer.junit5.PactConsumerTestExt
import au.com.dius.pact.consumer.junit5.PactTestFor
import au.com.dius.pact.core.model.RequestResponsePact
import au.com.dius.pact.core.model.annotations.Pact
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith

@ExtendWith(PactConsumerTestExt::class)
@PactTestFor(providerName = "message-provider", port = "8888")
class ProviderClientPactTest {

    @Pact(provider = "message-provider", consumer = "message-consumer")
    fun createPact(builder: PactDslWithProvider): RequestResponsePact {
        return builder
                .given("a valid message provider")
                .uponReceiving("a valid parameter")
                .path("/messages")
                .method("GET")
                .matchQuery("parameter", "\\w*", "someParameter")
                .willRespondWith()
                .status(200)
                .headers(mapOf("Content-type" to "application/json"))
                .body(PactDslJsonBody().stringType("message", "someMessage"))
                .toPact()
    }

    @Test
    @PactTestFor(pactMethod = "createPact")
    fun getResponse(mockServer: MockServer) {
        val client = ProviderClient(mockServer.getUrl() + "/messages")
        val response = client.getResponse("someParameter")
        assertEquals(response.message, "someMessage")
    }
}
  • Implementing client
class ProviderClient(private val messagesUrl: String) {

    private val objectMapper = ObjectMapper().registerKotlinModule()

    fun getResponse(parameter: String): Response {
        val response = khttp.get(
                url = messagesUrl,
                params = mapOf("parameter" to parameter)
        ).text

        return objectMapper.readValue(response, Response::class.java)
    }
}

data class Response(val message: String)
  • Running tests
cd consumer
./gradlew test

After the test is successful, a pact file will be generated on ../pacts folder, following the pattern <consumer-name>-<provider-name>.json. All the interactions detailed on consumer will be described in this file.

Validating the pact file in a SpringBootTest

Now with a generated pact file, you have to verify if the provider can fulfill the requirements.

  • Adding dependencies
dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	testImplementation("org.springframework.boot:spring-boot-starter-test") {
		exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
	}
	testImplementation("org.junit.jupiter:junit-jupiter:5.4.2")
	testImplementation("org.junit.jupiter:junit-jupiter-engine:5.5.2")
	testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.2")
	testImplementation("org.assertj:assertj-core:3.9.1")
	testImplementation("au.com.dius.pact.provider:junit5:4.1.0")
}
  • Writing pact provider test
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Provider("message-provider")
@PactFolder("../pacts")
@ExtendWith(PactVerificationInvocationContextProvider::class)
class MessageProviderPactTest {

    @LocalServerPort
    var serverPort: Int = 0

    @BeforeEach
    fun before(context: PactVerificationContext) {
        context.target = HttpTestTarget(
                host = "localhost",
                port = serverPort,
                path = "/"
        )
    }

    @State("a valid message provider")
    fun setupProvider() {
    }

    @TestTemplate
    fun pactVerificationTestTemplate(context: PactVerificationContext) {
        context.verifyInteraction()
    }

}
  • Implementing the endpoint for interaction
@RestController
@RequestMapping("/messages")
class MessageResource {

    @GetMapping
    fun getMessage(@RequestParam("parameter") parameter: String): Response {
        return Response("any message in here")
    }

}

data class Response(val message: String)
  • Verifying the pacts
cd provider-spring
./gradlew test

In the build log you can assert that the interaction was properly implemented. A better understanding is achieved by modifying both sides and running the test again.

How to share the pacts with no physical dependencies?

For that you will need to implement a pact-broker. Pact-broker will allow you to share the pacts and verification results if needed.

Ongoing Quarkus provider verification

An attempt to validate pacts using QuarkusTest is placed on provider-quarkus folder. Unfortunately, QuarkusTest-Extension is not working properly with pact-jvm-provider library. For more information please visit: