spring-cloud-contract
spring-cloud-contract copied to clipboard
`messageBody` mangles json-ish inputs
I'm trying to use Spring Cloud Contract to test Messaging (Spring Cloud Stream over Kafka). The message I need to send is JSON encoded, and one of its fields is a JSON encoded string, like this:
{
"type": "x",
"payload": "{\"a\": 4}"
}
Using Spring Cloud Contract Maven Plugin, versions 2.2.6.RELEASE
or 3.0.1
.
This is the contract file - node body is provided as a String:
import org.springframework.cloud.contract.spec.Contract
Contract.make{
input {
messageBody(' {"type": "x","payload": "{\\"a\\": 4}"}')
}
}
and this is the generated test class (trimmed for focus):
public class Sunny_dayTest extends AsyncBaseClass {
@Inject ContractVerifierMessaging contractVerifierMessaging;
@Inject ContractVerifierObjectMapper contractVerifierObjectMapper;
@Test
public void validate_notifyChangeUpdate() throws Exception {
// given:
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
"{\"type\":\"x\",\"payload\":{\"a\":4}}"
, headers()
);
// when:;
}
}
The message in the Java file is altered from my input; Here it is printed and formatted:
{
"type": "x",
"payload": {
"a": 4
}
}
(Note that value of payload
is now an object, not a string!)
It appears that the value of messageBody
is being recursively parsed and reassembled.
Some other values are also modified:
messageBody(' [][]]')
becomes "[]"
(leading whitespace removed, as well as all content that can't be parsed as json).
And, hilariously, messageBody(file('message.json'))
translates into ...create("new String(fileToBytes(this, \"notifyChangeUpdate_request_message.json\")), ...)"
Is there a way to provide an arbitrary string to messageBody
and have it used as the message?
I see the problem... maybe try sending it as bytes?
messageBody('{"type": "x"}'.getBytes())
generates:
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
"[123,34,116,121,112,101,34,58,32,34,120,34,125]"
, headers()
...which is also not exactly right for me.
no no, fileAsBytes()
not .getBytes()
Ah, yeah, filesAsBytes()
seems to actually work:
messageBody(fileAsBytes('message.json') )
-->
contractVerifierMessaging.create(
fileToBytes(this, "notifyChangeUpdate_withbug_request_message.json")
, headers()
thanks :)
Cool, can we close this? :)
Are you OK with this behavior? especially the one with file()
?
The problem is that you have a JSON in a JSON. This is why we're parsing the JSON as JSON and your JSON in payload is also parsed. It's hard to fix really :grimacing: So I think that if you have the case where you don't want this to be parsed as we do then you should use the approach with fileAsBytes(...)
.
Why would you parse JSON at all?
This can also show up in other strange places:
{
"methodName": "numbers",
"returnValue": "[]int"
}
will remove int
from the returnValue
types ([]int
is a type in Go-lang).
If the input is a String, why bother manipulating it?
Maybe it's a bug then - I can't do the investigation right now :grimacing:
Is this also a problem in version 4.0.x of Spring Cloud Contract? We've changed the messaging contracts to only include the triggeredBy
and outputMessage
parts.
With this commit https://github.com/spring-cloud/spring-cloud-contract/commit/a2444550d546f9f5663074d280475e72ed8cdf1d we're suggesting that whenever you have payload that contains an embedded JSON as part of your json, you should go with the fileAsBytes
approach and then you won't have any additional JSON assertions.