I have written an integration test using Citrus Framework for a Spring Boot service that communicates via HTTP REST.
I was able to embed some of the Citrus validation "methods" in the JSON to handle situations like @isNumber()@
for timestamps that are always changing. I ran into a problem, however, when I attempted some others.
There is another field (commandID
) in the JSON which contains a UUID and is likely to change from one invocation to another. I first decided to use @match("<regex>")@
to match a UUID pattern, and when that produced a problem, switched to trying to use @ignore@
, but that also produced the same problem.
Here is the error from the console:
13:29:21.962 [main] ERROR com.consol.citrus.report.LoggingReporter - TEST FAILED MissionPlannerIT.testPlanMission <edu.mit.ll.mission_services.service.mission_planner> Nested exception is:
com.consol.citrus.exceptions.TestCaseFailedException: Validation failed: Values not equal for header element 'Accept', expected 'application/json' but was 'application/json,application/*+json'
I don't know what the issue is here, nor how to fix it.
gen-route-command.json:
{
"header": {
"timestamp": "@isNumber()@",
"typeID": "edu.mit.ll.mission_services.messages.GenerateRouteCommand",
"transaction": {
"id": 1,
"startTime": "@isNumber()@"
},
"signature": {
"algorithm": null,
"keySize": 0,
"keyValue": null,
"sender": null
}
},
"commandID": "@ignore@",
"commandType": "GENERATE_ROUTE"
}
The @isNumber()@
functions work and allow the matching of those elements to pass validation. @ignore@
, however does not. I have tried with both variations: @ignore@
and @ignore()@
, neither works. As mentioned earlier, @match(...)@
also does not work.
UPDATE:
I modified things so that I try to do the validation in the test case code. I changed the value in the JSON back to a UUID.
runner.http(builder -> builder
.server(routeGeneratorServer)
.receive()
.post("/v1/missionServices/missionPlanning/generateRoute")
.accept(ContentType.APPLICATION_JSON.getMimeType())
.payload(new ClassPathResource("templates/gen-route-command.json"))
.validate("$.commandID", "@ignore@"));
gen-route-command.json:
{
"header": {
"timestamp": "@isNumber()@",
"typeID": "edu.mit.ll.mission_services.messages.GenerateRouteCommand",
"transaction": {
"id": 1,
"startTime": "@isNumber()@"
},
"signature": {
"algorithm": null,
"keySize": 0,
"keyValue": null,
"sender": null
}
},
"commandID": "0710d523-43da-4f68-90c7-a2b4544a955d",
"commandType": "GENERATE_ROUTE"
}
Unfortunately, this does not work. I get the error that I originally was trying to mitigate with @ignore@
or @match(...)@
.
14:12:57.299 [main] ERROR com.consol.citrus.report.LoggingReporter - TEST FAILED MissionPlannerIT.testPlanMission <edu.mit.ll.mission_services.service.mission_planner> Nested exception is:
com.consol.citrus.exceptions.TestCaseFailedException: Failed to validate JSON text:
{"header":{"timestamp":1581016372132,"typeID":"edu.mit.ll.mission_services.messages.GenerateRouteCommand","transaction":{"id":1,"startTime":1581016372130},"signature":{"algorithm":null,"keySize":0,"keyValue":null,"sender":null}},"commandID":"9302ebde-894b-43a3-b7a3-92a158c7170e","commandType":"GENERATE_ROUTE"} Values not equal for entry: 'commandID', expected '0710d523-43da-4f68-90c7-a2b4544a955d' but was '9302ebde-894b-43a3-b7a3-92a158c7170e'
The commandID
values are different because it generates a new UUID each run.
It appears that the validation in the test does not "ignore" the value and instead the JSON validation flags the UUID differences.
It would seem that the Citrus "functions" embedded in the JSON were not the problem. The problem was that I was not setting the Accept
and Content-Type
headers in the message the service was POSTing for the Citrus receive call. Since the receive call was set up to look for message type application/json
, it was failing because my message had no headers at all (this is my impression).
The Citrus call:
// Set route generator to receive and validate generate route command.
runner.http(builder -> builder
.server(routeGeneratorServer)
.receive()
.post("/v1/missionServices/missionPlanning/generateRoute")
.accept(ContentType.APPLICATION_JSON.getMimeType())
.payload(new ClassPathResource("templates/gen-route-command.json")));
The "generate route command" message it received had no headers defined. Once I set the Accept
and Content-Type
headers to application/json
, the test was able to proceed.