Introduction
This blogpost shows how to create a
Pact provider test using:
- Pact 4.0.0
- AWS Serverless Lambda
- Java 11
- JUnit 5 (Jupiter)
Example Provider Test
Below is the example test class. Note how a mock service is used to simulate the endpoint defined (also) in serverless.yml.
package com.ttlnews.pact.tests.provider;
import au.com.dius.pact.provider.junit.Provider;
import au.com.dius.pact.provider.junit.State;
import au.com.dius.pact.provider.junit.loader.PactBroker;
import au.com.dius.pact.provider.junit.loader.PactBrokerAuth;
import au.com.dius.pact.provider.junit5.HttpTestTarget;
import au.com.dius.pact.provider.junit5.PactVerificationContext;
import au.com.dius.pact.provider.junit5.PactVerificationInvocationContextProvider;
import com.amazonaws.services.lambda.runtime.Context;
import lombok.extern.slf4j.Slf4j;
import net.jcip.annotations.NotThreadSafe;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockserver.integration.ClientAndServer;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockserver.integration.ClientAndServer.startClientAndServer;
import static org.mockserver.matchers.Times.exactly;
import static org.mockserver.model.Header.header;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import static org.mockserver.model.JsonBody.json;
/**
* Java 11 serverless provider Pact contract provider implementation using Pact with Junit5 (Jupiter) for provider SERVICE_A.
*
* Dependencies needed:
*
<dependency>
<groupId>au.com.dius</groupId>
<artifactId>pact-jvm-consumer-junit5</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>au.com.dius</groupId>
<artifactId>pact-jvm-provider-junit5</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
*
*
*/
@Provider(SERVICE_A)
// Below environment variables must be set when running this test
@PactBroker(host = "${PACT_HOST}",
authentication = @PactBrokerAuth(username = "${PACT_HOST_USERNAME}", password = "${PACT_HOST_PASSWD}"))
@NotThreadSafe // Pact contract tests can't seem to handle methods running in parallel, so prevent maven failsafe/surefire plugin to run Pact tests in parallel
@Slf4j
public class ServiceAProviderTest {
private static final String SERVICE_A = "service-A";
private Context lambdaContext;
private SomeRepository someRepository;
private ClientAndServer mockClientAndServer;
@BeforeEach
void before(PactVerificationContext context) {
lambdaContext = mock(Context.class);
someRepository = new InMemoryMockedRepo();
mockClientAndServer = startClientAndServer(8888);
context.setTarget(new HttpTestTarget("127.0.0.1", 8888));
}
@AfterEach
void stopServer() {
mockClientAndServer.stop();
}
// This triggers the defined contract test(s) at the host PACT_HOST
@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context) {
context.verifyInteraction();
}
@State("Create a new resource A")
public void shouldCreateResourceA() {
log.info("Set up state");
// Given
// Maybe some more mocking of 'lambdaContext' needed, depending on your case
// Lambda being tested
CreateResourceALambda createResourceALambda = new CreateResourceA(someRepository);
CreateResourceALambdaRequest createResourceALambdaRequest = CreateResourceALambdaRequest.builder()
.someValue("25")
.build();
// When
final LambdaResult lambdaResult = createResourceALambda.executeRequest(createResourceALambdaRequest, lambdaContext);
final String body = lambdaResult.getBody();
assertNotNull(body);
// Prepare the mockserver to return what the lambda returns when it is invoked (set up above in the 'body' variable)
// Of course the path to this CreateResourceALambda is defined in the serverless.yml, but that we can't access now. So need to repeat that
// endpoint here.
mockClientAndServer.when(
request()
.withMethod("POST")
.withPath("/resources/")
.withHeaders(
header("x-request-trace-id"), // Any value is fine
header("Authorization"), // Any value is fine
header("Content-Type", "application/json")
)
.withBody(json("{someValue: '25'}")) // Indeed need to use this strange JSON format
,
exactly(1))
.respond(
// Response as defined by the matching Pact consumer test; in this case found at host PACT_HOST
response()
.withStatusCode(201)
.withHeaders(
header("Content-type", "application/json; charset=utf-8"),
header("Authorization") // Any value is fine
)
.withBody(body)
);
}
}
Migrating from your Pact tests from JUnit4 to Junit5 can be found
here.
Note to self: use https://www.opinionatedgeek.com/codecs/htmlencoder to encode code, open the HTML view, and then wrap the code in <pre> open en close tag.
No comments:
Post a Comment