3

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 Answers2

2

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.

Peter Thomas
  • 54,465
  • 21
  • 84
  • 248
  • This helps me when performing a match in a feature file itself, i.e. the JSON is "in-line". The particular use-case I'm thinking of I guess is more similar to the fuzzy-matchers we have in JSON read from file. E.g. #regex. You could think of it as #myCustomFunction(_,'FORMAT'). I'm wary this might not be doable. PS: no magician needed ;) Totally on board with karate's mission. I'm writing a framework on top of Karate and pushing for buy in from my team. I 100% agree with DAMP > DRY by the way. I just thought doing something like this would be a nice way to supplement schema validation. – karatekid5088 Jul 15 '20 at 06:39
  • 1
    OK - ignore the above. Turns out it can be done in the JSON file itself - but the assertion failed because I had 'expected' and 'actual' the wrong way round :D I'll show this fully in an answer. Thanks for the help Peter. – karatekid5088 Jul 15 '20 at 06:59
  • 1
    @karatekid5088 `I'm writing a framework on top of Karate and pushing for buy in from my team` - great. my advice is don't go overboard, and read this also: https://stackoverflow.com/a/58339662/143475 – Peter Thomas Jul 15 '20 at 07:26
  • 1
    Thanks a lot for this - very useful. Yep I totally agree - the reason I started looking at Karate was due to the scars I still have from a previous project, where I was maintaining the very approach you warn against :) These custom common libs cause more harm than good. I like to think of Karate as "the common lib" itself. – karatekid5088 Jul 15 '20 at 07:56
2

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.