-
-
Notifications
You must be signed in to change notification settings - Fork 482
Description
TLDR: Having a single test case for multiple message types to the same parser cause fast failing of the test and make it hard to determine, which message was faulty. It would be easier to have one test per interaction, which is currently only possible when defining that manually.
Assuming I got that right, the currently recommended way to pass multiple interactions of the same message parser by referencing all interactions described by @Pact
using the pactMethods
annotation. The messages will then be passed as a list to the @PactFor
method. This has two major consequences for failed parsing.
- The test is stopped as soon as the first message fails to parse. This can be circumvented by Framework tools like JUnit's
assertAll
, but isn't very nice nevertheless. - It is very hard to detect which message failed to parse, as long as that isn't inferable from the stack trace. There is also very little information within the
@PactFor
method which one of the parsed messages corresponds to which of the interactions. The only point of reference is the current index of the loop on the message list. The content cannot be used to infer information, as it already has been converted to a byte array.
The only way to circumvent this currently (to my knowledge) is to define a @PactFor
method for each of the interactions. However, those methods would only differ in their name and their value for pactMethod
.
Solution Approach:
Use a similar solution to the Provider Test implementation relying on the @TestTemplate
annotation. I haven't work with this so far, so I don't know if that possible or which changes would be required.
An example based on https://github.com/pact-foundation/pact-jvm/blob/master/consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/V4AsyncMessageTest.groovy
@ExtendWith(PactConsumerTestExt)
@PactTestFor(providerName = 'MessageProvider', providerType = ProviderType.ASYNCH, pactVersion = PactSpecVersion.V4)
class V4AsyncMessageTest {
// Example message handler
static class MessageHandler {
static ProcessedMessage process(byte[] data) {
def json = new JsonSlurper().parse(data) as Map
new ProcessedMessage(json)
}
}
// Example processed message
@Canonical
static class ProcessedMessage {
String testParam1
String testParam2
}
/**
* Set the first message interaction (with matching rules)
*/
@Pact(consumer = 'test_consumer_v4')
V4Pact createPact(MessagePactBuilder builder) {
PactDslJsonBody body = new PactDslJsonBody()
body.stringMatcher('testParam1', '\\w+', 'value1')
body.stringValue('testParam2', 'value2')
Map<String, Object> metadata = [destination: Matchers.regexp('\\w+\\d+', 'X001')]
builder.given('SomeProviderState')
.expectsToReceive('a test message')
.withMetadata(metadata)
.withContent(body)
.toPact()
}
/**
* Setup the second message interaction (with plain data)
*/
@Pact(provider = 'MessageProvider', consumer = 'test_consumer_v4')
V4Pact createPact2(MessagePactBuilder builder) {
PactDslJsonBody body = new PactDslJsonBody()
body.stringValue('testParam1', 'value3')
body.stringValue('testParam2', 'value4')
Map<String, String> metadata = ['Content-Type': 'application/json']
builder.given('SomeProviderState2')
.expectsToReceive('a test message')
.withMetadata(metadata)
.withContent(body)
.toPact()
}
/**
* This is the new, comfortable part. the method would be invoked for each interaction. The test name could be generated from the value that is passed to `expectsToReceive` on the interaction, like it's done in both sync and async message provider tests.
*/
@TestTemplate
@ExtendWith(PactVerificationSpringProvider::class)
@WithMockUser
void test(PactInteractionContext context) {
assertDoesNotThrow(() -> {
new MessageHandler().process(context.getMessage().getAsString())
})
}
}
Edit: Fixed test template assertions to be actually applicable to both interactions.