Karate - custom assertion in expected JSON schema

Viewed 733

I'm looking to perform custom assertions on fields in JSON loaded from file.

I understand that we have fuzzy matching, but I'd like to perform something more custom e.g. have a function which parses a date as a LocalDateTime:

public class DateUtil {
public static boolean matchesMyDateFormat(String dateStr) {
    try {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        LocalDateTime.parse(dateStr, formatter);
    } catch (DateTimeParseException e) {
        return false;
    }
    return true;
}

}

This would be called by the following:

* def matchesMyDateFormat =
"""
function fn(dateX){
  return Java.type('com.karate.DateUtil').matchesMyDateFormat(dateX);
}
"""

* def expected = read('expected.json')
* def actual = read('actual.json')
* match expected == actual

Where expected.json looks like this:

{
   "date1" : "#? matchesMyDateFormat(_)"
}

NB this is specifically for JSON loaded from file and not on JSON which is specified in the feature file itself (e.g. like for isValidTime() here: https://github.com/intuit/karate/blob/master/karate-junit4/src/test/java/com/intuit/karate/junit4/demos/schema-like.feature).

Few reasons for wishing to do it this way:

  • Some of the payloads I need to assert have a lot of date fields coming back, with different formats. Asserting like the above would tie in nicely with Karate's excellent way of validating schema. Doing this in the feature files, however, would require a lot of code, i.e. a line of code for each date (I realize match each could be used - but even this would get complex, depending on the nesting of the fields.)
  • I would be able to add this function to my common utils feature file, so it can be re-used throughout the project's expected response schema.
  • Beyond this I'd be looking to do other things, like check that one date occurs before another (but I'd want to do this using various types in Java, e.g. taking time zone into consideration).
  • I'd also be looking for the format matching method to take in another param, which lets the tester specify a custom format string.

NB: I've read through the docs and the other SO answers related to date assertions and believe this is a slightly different ask.

Is the above possible to do in Karate at the moment?

2 Answers

You can add functions in karate-config.js which will be "global". For example:

var config = {};
config.isValidDate = read('classpath:is-valid-date.js');
return config;

Now you can use isValidDate(_) in any feature. Note that JS functions can take multiple arguments, e.g:

* match foo == { bar: "#? isValidDate(_, 'MYFORMAT')" }

In 0.9.6.RC4 we made improvements so that you can move complex conditional logic and even match operations into re-usable JS files: https://github.com/intuit/karate/issues/1202

Be warned, doing a lot of this may lead to un-readable tests: https://stackoverflow.com/a/54126724/143475

One hint, you can use karate.forEach() to extract all date-fields into an array and then a single match each may work.

Finally, if you still feel there is "too much code in your feature files", I don't know, maybe you need to consult a magician.

What I was trying was in fact a valid use-case (as is the alternative solution kindly suggested by Peter Thomas in his answer).

The reason my particular variation wasn't working was this error:

07:22:50.421 assertion failed: path: $.date1, actual: '#? matchesMyDateFormat(_)', expected: '2020-06-10T14:44:57.060Z', reason: not equal

I noticed with a fresh pair of eyes that I should flip the match statement from:

* match expected == actual

To:

* match actual == expected

This way is required in order for Karate to work its magic and call the custom function in expected.json.

Related