Skip to content

Commit

Permalink
feat: Support multiple test targets with JUnit 5 #1708
Browse files Browse the repository at this point in the history
  • Loading branch information
rholshausen committed Apr 22, 2024
1 parent 1df2b1f commit 2a4df7c
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import au.com.dius.pact.core.model.RequestResponseInteraction
import au.com.dius.pact.core.model.UnknownPactSource
import au.com.dius.pact.core.model.V4Interaction
import au.com.dius.pact.core.model.generators.GeneratorTestMode
import au.com.dius.pact.core.support.Json
import au.com.dius.pact.core.support.MetricEvent
import au.com.dius.pact.core.support.Metrics
import au.com.dius.pact.core.support.Result
import au.com.dius.pact.core.support.*
import au.com.dius.pact.core.support.expressions.SystemPropertyResolver
import au.com.dius.pact.core.support.expressions.ValueResolver
import au.com.dius.pact.provider.IConsumerInfo
Expand All @@ -25,6 +22,7 @@ import au.com.dius.pact.provider.VerificationResult
import au.com.dius.pact.provider.junitsupport.TestDescription
import io.pact.plugins.jvm.core.PluginConfiguration
import org.junit.jupiter.api.extension.ExtensionContext
import kotlin.collections.isNotEmpty

/**
* The instance that holds the context for the test of an interaction. The test target will need to be set on it in
Expand All @@ -40,7 +38,8 @@ data class PactVerificationContext @JvmOverloads constructor(
val consumer: IConsumerInfo,
val interaction: Interaction,
val pact: Pact,
var testExecutionResult: MutableList<VerificationResult.Failed> = mutableListOf()
var testExecutionResult: MutableList<VerificationResult.Failed> = mutableListOf(),
val additionalTargets: MutableList<TestTarget> = mutableListOf()
) {
val stateChangeHandlers: MutableList<Any> = mutableListOf()
var executionContext: MutableMap<String, Any>? = null
Expand Down Expand Up @@ -164,4 +163,22 @@ data class PactVerificationContext @JvmOverloads constructor(
fun addStateChangeHandlers(vararg stateClasses: Any) {
stateChangeHandlers.addAll(stateClasses)
}

/**
* Adds additional targets to the context for the test.
*/
fun addAdditionalTarget(target: TestTarget) {
additionalTargets.add(target)
}

fun currentTarget(): TestTarget? {
return if (target.supportsInteraction(interaction)) {
target
} else {
additionalTargets.firstOrNull { it.supportsInteraction(interaction) }
}
}
}

fun PactVerificationContext?.hasMultipleTargets() = if (this == null)
false else additionalTargets.isNotEmpty()
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import au.com.dius.pact.provider.VerificationFailureType
import au.com.dius.pact.provider.VerificationResult
import au.com.dius.pact.provider.junitsupport.VerificationReports
import au.com.dius.pact.provider.reporters.ReporterManager
import io.pact.plugins.jvm.core.InteractionVerificationData
import io.github.oshai.kotlinlogging.KLogging
import org.apache.hc.core5.http.ClassicHttpRequest
import org.apache.hc.core5.http.HttpRequest
Expand Down Expand Up @@ -79,10 +78,10 @@ open class PactVerificationExtension(
return when (parameterContext.parameter.type) {
Pact::class.java -> true
Interaction::class.java -> true
ClassicHttpRequest::class.java, HttpRequest::class.java -> testContext?.target is HttpTestTarget
ClassicHttpRequest::class.java, HttpRequest::class.java -> testContext.hasMultipleTargets() || testContext?.currentTarget() is HttpTestTarget
PactVerificationContext::class.java -> true
ProviderVerifier::class.java -> true
RequestData::class.java -> testContext?.target is PluginTestTarget
RequestData::class.java -> testContext.hasMultipleTargets() || testContext?.currentTarget() is PluginTestTarget
else -> false
}
}
Expand Down Expand Up @@ -127,26 +126,34 @@ open class PactVerificationExtension(
val store = context.getStore(namespace)
val testContext = store.get("interactionContext") as PactVerificationContext

val providerInfo = testContext.target.getProviderInfo(serviceName, pactSource)
val target = testContext.currentTarget()
?: throw UnsupportedOperationException(
"No test target has been configured for ${interaction.javaClass.simpleName} interactions")
val providerInfo = target.getProviderInfo(serviceName, pactSource)
testContext.providerInfo = providerInfo

prepareVerifier(testContext, context, pactSource)
prepareVerifier(testContext, context, pactSource, target)
store.put("verifier", testContext.verifier)

val executionContext = testContext.executionContext ?: mutableMapOf()
executionContext["ArrayContainsJsonGenerator"] = ArrayContainsJsonGenerator
val requestAndClient = testContext.target.prepareRequest(pact, interaction, executionContext)
val requestAndClient = target.prepareRequest(pact, interaction, executionContext)
if (requestAndClient != null) {
val (request, client) = requestAndClient
store.put("request", request)
store.put("client", client)
if (testContext.target.isHttpTarget()) {
if (target.isHttpTarget()) {
store.put("httpRequest", request)
}
}
}

private fun prepareVerifier(testContext: PactVerificationContext, extContext: ExtensionContext, pactSource: au.com.dius.pact.core.model.PactSource) {
private fun prepareVerifier(
testContext: PactVerificationContext,
extContext: ExtensionContext,
pactSource: au.com.dius.pact.core.model.PactSource,
target: TestTarget
) {
val consumer = when {
pactSource is BrokerUrlSource && pactSource.result != null -> ConsumerInfo(pactSource.result!!.name,
pactSource = pactSource, notices = pactSource.result!!.notices, pending = pactSource.result!!.pending)
Expand All @@ -155,7 +162,7 @@ open class PactVerificationExtension(

val verifier = ProviderVerifier()
verifier.verificationSource = "junit5"
testContext.target.prepareVerifier(verifier, extContext.requiredTestInstance, pact)
target.prepareVerifier(verifier, extContext.requiredTestInstance, pact)

setupReporters(verifier, serviceName, interaction.description, extContext, testContext.valueResolver)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ class PluginTestTarget(private val config: MutableMap<String, Any?> = mutableMap
return false
}

override fun supportsInteraction(interaction: Interaction) = interaction.isV4()

override fun executeInteraction(client: Any?, request: Any?): ProviderResponse {
return ProviderResponse()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ interface TestTarget {
* Prepares the verifier for use during the test
*/
fun prepareVerifier(verifier: IProviderVerifier, testInstance: Any, pact: Pact)

/**
* If the test target supports the given interaction
*/
fun supportsInteraction(interaction: Interaction): Boolean = false
}

/**
Expand Down Expand Up @@ -100,6 +105,8 @@ open class HttpTestTarget @JvmOverloads constructor (

override fun prepareVerifier(verifier: IProviderVerifier, testInstance: Any, pact: Pact) { }

override fun supportsInteraction(interaction: Interaction) = interaction is SynchronousRequestResponse

override fun executeInteraction(client: Any?, request: Any?): ProviderResponse {
val providerClient = client as ProviderClient
val httpRequest = request as HttpUriRequest
Expand Down Expand Up @@ -217,6 +224,9 @@ open class MessageTestTarget @JvmOverloads constructor(
}
}

override fun supportsInteraction(interaction: Interaction) = interaction is MessageInteraction ||
interaction is V4Interaction.SynchronousMessages

override fun executeInteraction(client: Any?, request: Any?): ProviderResponse {
return ProviderResponse(200)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import au.com.dius.pact.core.model.Request
import au.com.dius.pact.core.model.RequestResponseInteraction
import au.com.dius.pact.core.model.RequestResponsePact
import au.com.dius.pact.core.model.Response
import au.com.dius.pact.core.model.V4Interaction
import au.com.dius.pact.core.support.Result
import au.com.dius.pact.core.support.expressions.ValueResolver
import au.com.dius.pact.provider.IConsumerInfo
Expand Down Expand Up @@ -42,7 +43,7 @@ class PactVerificationExtensionSpec extends Specification {
@Shared ExtensionContext extContext
@Shared Map<String, Object> contextMap
ValueResolver mockValueResolver
@Shared RequestResponseInteraction interaction1, interaction2
@Shared Interaction interaction1, interaction2
@Shared RequestResponsePact pact
PactBrokerSource pactSource
@Shared ClassicHttpRequest classicHttpRequest
Expand Down Expand Up @@ -173,8 +174,11 @@ class PactVerificationExtensionSpec extends Specification {

def 'supports parameter test'() {
given:
context.target = target
extension = new PactVerificationExtension(pact, pactSource, interaction1, 'service', 'consumer',
def interaction = new V4Interaction.SynchronousHttp(null, 'interaction2')
context = new PactVerificationContext(store, extContext, target, Stub(IProviderVerifier),
Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction, pact, [])

extension = new PactVerificationExtension(pact, pactSource, interaction, 'service', 'consumer',
mockValueResolver)
Parameter parameter = mock(Parameter)
when(parameter.getType()).thenReturn(parameterType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,5 +217,7 @@ class MockMvcTestTarget @JvmOverloads constructor(
/* NO-OP */
}

companion object : KLogging()
override fun supportsInteraction(interaction: Interaction) = interaction is SynchronousRequestResponse

companion object : KLogging()
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package au.com.dius.pact.provider.spring.junit5

import au.com.dius.pact.core.model.ContentType
import au.com.dius.pact.core.model.IRequest
import au.com.dius.pact.core.model.OptionalBody
import au.com.dius.pact.core.model.Pact
import au.com.dius.pact.core.model.PactSource
import au.com.dius.pact.core.model.*
import au.com.dius.pact.provider.IProviderVerifier
import au.com.dius.pact.provider.ProviderInfo
import au.com.dius.pact.provider.ProviderResponse
Expand Down Expand Up @@ -115,4 +111,6 @@ interface WebFluxBasedTestTarget : TestTarget {

return uriBuilder.toUriString()
}
}

override fun supportsInteraction(interaction: Interaction) = interaction is SynchronousRequestResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,5 +217,7 @@ class Spring6MockMvcTestTarget @JvmOverloads constructor(
/* NO-OP */
}

companion object : KLogging()
override fun supportsInteraction(interaction: Interaction) = interaction is SynchronousRequestResponse

companion object : KLogging()
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package au.com.dius.pact.provider.spring.spring6

import au.com.dius.pact.core.model.ContentType
import au.com.dius.pact.core.model.IRequest
import au.com.dius.pact.core.model.OptionalBody
import au.com.dius.pact.core.model.Pact
import au.com.dius.pact.core.model.PactSource
import au.com.dius.pact.core.model.*
import au.com.dius.pact.provider.IProviderVerifier
import au.com.dius.pact.provider.ProviderInfo
import au.com.dius.pact.provider.ProviderResponse
Expand Down Expand Up @@ -115,4 +111,6 @@ interface WebFluxBasedTestTarget : TestTarget {

return uriBuilder.toUriString()
}
}

override fun supportsInteraction(interaction: Interaction) = interaction is SynchronousRequestResponse
}

0 comments on commit 2a4df7c

Please sign in to comment.