Skip to content

Commit 3bb4a08

Browse files
committed
feat: Support mixing pact and non-pact test methods with @PactIgnore annotation #1674
1 parent 92018c0 commit 3bb4a08

File tree

6 files changed

+134
-33
lines changed

6 files changed

+134
-33
lines changed

consumer/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,3 +653,9 @@ and [#1383](https://github.com/pact-foundation/pact-jvm/issues/1383).
653653
One option (if the HTTP client supports it, Apache HTTP Client does) is to set the system property `http.keepAlive` to `false` in
654654
the test JVM. The other option is to set `pact.mockserver.addCloseHeader` to `true` to force the mock server to
655655
send a `Connection: close` header with every response (supported with Pact-JVM 4.2.7+).
656+
657+
# Mixing Pact and non-Pact test methods in the same test class
658+
659+
By default, the Pact lifecycle will be invoked for every test method and will expect there to be a method annotated
660+
with `@Pact` for each test method invoked. To add non-Pact tests, just annotate the non-Pact test method with the
661+
`@PactIgnore` annotation.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package au.com.dius.pact.consumer.junit5
2+
3+
/**
4+
* Marks a injected MockServer parameter as for a particular provider. This is used when there is more than one
5+
* provider setup for the test.
6+
*/
7+
@Target(AnnotationTarget.VALUE_PARAMETER)
8+
annotation class ForProvider(val value: String)
9+
10+
/**
11+
* Marks a test as a non-pact test. This will cause the normal Pact lifecycle to be skipped for that test.
12+
*/
13+
@Target(AnnotationTarget.FUNCTION)
14+
annotation class PactIgnore

consumer/junit5/src/main/kotlin/au/com/dius/pact/consumer/junit5/ForProvider.kt

Lines changed: 0 additions & 3 deletions
This file was deleted.

consumer/junit5/src/main/kotlin/au/com/dius/pact/consumer/junit5/PactConsumerTestExt.kt

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -210,20 +210,28 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal
210210
}
211211

212212
override fun beforeTestExecution(context: ExtensionContext) {
213-
for ((providerInfo, pactMethod) in lookupProviderInfo(context)) {
214-
logger.debug { "providerInfo = $providerInfo" }
215-
216-
if (mockServerConfigured(context) ||
217-
providerInfo.providerType == null ||
218-
providerInfo.providerType == ProviderType.SYNCH ||
219-
providerInfo.providerType == ProviderType.UNSPECIFIED) {
220-
val mockServer = setupMockServerForProvider(providerInfo, pactMethod, context)
221-
mockServer.start()
222-
mockServer.waitForServer()
213+
if (!ignoredTest(context)) {
214+
for ((providerInfo, pactMethod) in lookupProviderInfo(context)) {
215+
logger.debug { "providerInfo = $providerInfo" }
216+
217+
if (mockServerConfigured(context) ||
218+
providerInfo.providerType == null ||
219+
providerInfo.providerType == ProviderType.SYNCH ||
220+
providerInfo.providerType == ProviderType.UNSPECIFIED
221+
) {
222+
val mockServer = setupMockServerForProvider(providerInfo, pactMethod, context)
223+
mockServer.start()
224+
mockServer.waitForServer()
225+
}
223226
}
224227
}
225228
}
226229

230+
private fun ignoredTest(context: ExtensionContext): Boolean {
231+
return context.testMethod.isPresent &&
232+
AnnotationSupport.isAnnotated(context.testMethod.get(), PactIgnore::class.java)
233+
}
234+
227235
private fun setupMockServerForProvider(
228236
providerInfo: ProviderInfo,
229237
pactMethod: String,
@@ -527,30 +535,32 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal
527535
}
528536

529537
override fun afterTestExecution(context: ExtensionContext) {
530-
val store = context.getStore(NAMESPACE)
538+
if (!ignoredTest(context)) {
539+
val store = context.getStore(NAMESPACE)
531540

532-
val providers = store["providers"] as List<Pair<ProviderInfo, String>>
533-
for ((provider, _) in providers) {
534-
val pact = store["pact:${provider.providerName}"] as BasePact?
535-
Metrics.sendMetrics(MetricEvent.ConsumerTestRun(pact?.interactions?.size ?: 0, "junit5"))
536-
}
541+
val providers = store["providers"] as List<Pair<ProviderInfo, String>>
542+
for ((provider, _) in providers) {
543+
val pact = store["pact:${provider.providerName}"] as BasePact?
544+
Metrics.sendMetrics(MetricEvent.ConsumerTestRun(pact?.interactions?.size ?: 0, "junit5"))
545+
}
537546

538-
for ((provider, _) in providers) {
539-
if (store["mockServer:${provider.providerName}"] != null) {
540-
val mockServer = store["mockServer:${provider.providerName}"] as JUnit5MockServerSupport
541-
Thread.sleep(100) // give the mock server some time to have consistent state
542-
mockServer.close()
543-
val result = mockServer.validateMockServerState(null)
544-
if (result is PactVerificationResult.Ok) {
547+
for ((provider, _) in providers) {
548+
if (store["mockServer:${provider.providerName}"] != null) {
549+
val mockServer = store["mockServer:${provider.providerName}"] as JUnit5MockServerSupport
550+
Thread.sleep(100) // give the mock server some time to have consistent state
551+
mockServer.close()
552+
val result = mockServer.validateMockServerState(null)
553+
if (result is PactVerificationResult.Ok) {
554+
if (!context.executionException.isPresent) {
555+
storePactForWrite(store, provider, mockServer)
556+
}
557+
} else {
558+
JUnitTestSupport.validateMockServerResult(result)
559+
}
560+
} else if (provider.providerType == ProviderType.ASYNCH || provider.providerType == ProviderType.SYNCH_MESSAGE) {
545561
if (!context.executionException.isPresent) {
546-
storePactForWrite(store, provider, mockServer)
562+
storePactForWrite(store, provider, null)
547563
}
548-
} else {
549-
JUnitTestSupport.validateMockServerResult(result)
550-
}
551-
} else if (provider.providerType == ProviderType.ASYNCH || provider.providerType == ProviderType.SYNCH_MESSAGE) {
552-
if (!context.executionException.isPresent) {
553-
storePactForWrite(store, provider, null)
554564
}
555565
}
556566
}

consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/PactConsumerTestExtSpec.groovy

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ class PactConsumerTestExtSpec extends Specification {
258258
@MockServerConfig(providerName = 'a', port = '1238')
259259
@MockServerConfig(providerName = 'b', port = '1239')
260260
def pactTestForMethod() { }
261+
262+
@PactIgnore
263+
def nonPactTestMethod() { }
261264
}
262265

263266
def 'mockServerConfigured - returns true when there are multiple MockServerConfig annotations on the test class'() {
@@ -352,4 +355,22 @@ class PactConsumerTestExtSpec extends Specification {
352355
then:
353356
config.port == 1239
354357
}
358+
359+
def 'ignoredTest - returns false for a normal test method'() {
360+
given:
361+
requiredTestClass = TestClass6
362+
testMethod = TestClass6.getMethod('pactTestForMethod')
363+
364+
expect:
365+
!testExt.ignoredTest(mockContext)
366+
}
367+
368+
def 'ignoredTest - returns true for a test method annotated with PactIgnore'() {
369+
given:
370+
requiredTestClass = TestClass6
371+
testMethod = TestClass6.getMethod('nonPactTestMethod')
372+
373+
expect:
374+
testExt.ignoredTest(mockContext)
375+
}
355376
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package au.com.dius.pact.consumer.junit5;
2+
3+
import au.com.dius.pact.consumer.MockServer;
4+
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
5+
import au.com.dius.pact.core.model.PactSpecVersion;
6+
import au.com.dius.pact.core.model.RequestResponsePact;
7+
import au.com.dius.pact.core.model.annotations.Pact;
8+
import org.apache.hc.client5.http.fluent.Request;
9+
import org.apache.hc.core5.http.ContentType;
10+
import org.apache.hc.core5.http.HttpResponse;
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.extension.ExtendWith;
13+
14+
import java.io.IOException;
15+
16+
import static au.com.dius.pact.consumer.dsl.LambdaDsl.newJsonArrayMinLike;
17+
import static org.hamcrest.MatcherAssert.assertThat;
18+
import static org.hamcrest.Matchers.is;
19+
import static org.hamcrest.Matchers.not;
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
22+
@ExtendWith(PactConsumerTestExt.class)
23+
@PactTestFor(pactVersion = PactSpecVersion.V3)
24+
class MixedPactAndNonPactTest {
25+
@Pact(consumer = "ApiConsumer")
26+
public RequestResponsePact defaultValues(PactDslWithProvider builder) {
27+
return builder
28+
.uponReceiving("GET request to retrieve default values")
29+
.path("/api/test")
30+
.willRespondWith()
31+
.status(200)
32+
.body(newJsonArrayMinLike(1, values -> values.object(value -> {
33+
value.numberType("id", 32432);
34+
value.stringType("name", "testId254");
35+
value.numberType("size", 1445211);
36+
}
37+
)).build())
38+
.toPact();
39+
}
40+
41+
@Test
42+
@PactTestFor(pactMethod = "defaultValues")
43+
void testDefaultValues(MockServer mockServer) throws IOException {
44+
HttpResponse response = Request.get(mockServer.getUrl() + "/api/test").execute().returnResponse();
45+
assertEquals(response.getCode(), 200);
46+
}
47+
48+
@Test
49+
@PactIgnore
50+
void nonPactTest() {
51+
assertThat(true, is(not(false))); // otherwise, the universe will end
52+
}
53+
}

0 commit comments

Comments
 (0)