Validating Json Data Edit on GitHub


Note! This feature was added in Storyteller 4.2

While it may be easier in many cases to deserialize the Json string into a .Net object and validate against that, in other cases you may only have the raw Json. Fortunately, Storyteller now includes a base class called JsonComparisonFixture that you can subclass in your Storyteller specification project to verify the expected values within a JSON document. Behind the scenes, JsonComparisonFixture heavily uses the JSONPath support in Newtonsoft.Json to pick out values within the Json structure.

For a quick example, here's a simplistic sample that verifies the contents of a simple Address Json document:

public class AddressJsonFixture : JsonComparisonFixture
{
    public override void SetUp()
    {
        // Before you call any assertions, you'll need
        // to attach the Json string to the current
        // JsonComparisonFixture
        var json = fetchJson();
        StoreJson(json);
    }

    private string fetchJson()
    {
        return "{\"city\": \"Austin\", \"distance\": \"5\"}";
    }

    public IGrammar CityIs()
    {
        return CheckJsonValue<string>("$.city", "The city should be {city}");
    }

    public IGrammar DistanceIs()
    {
        return CheckJsonValue<int>("$.distance", "The distance should be {distance}");
    }
}

Checking a Single Typed Value

To verify a single value in a Json document, use the CheckJsonValue() method shown below to create a declarative grammar:

public IGrammar CheckValue()
{
    return CheckJsonValue<int>("$.number", "The current number should be {number}");
}

Simple types are extracted from the Json document, parsed to the declared type, and then compared. String's just do an exact comparison to the raw Json value. Finally, any other type is deserialized via Newtonsoft.Json from the Json object matching the specified JSONPath. An example of this is verifying an array of integers like this:

public IGrammar CheckNumberArray()
{
    return CheckJsonValue<int[]>("$.numbers", "The number array should be {numbers}");
}

In usage, that grammar looks like this in a sample specification:

Checking Arrays

Verifying Json Data

The json is {"numbers": [1, 2, 3, 5, 8], "names":["Justin", "Tamba", "Spencer"]}

This should succeed

The number array should be 1, 2, 3, 5, 8

This should fail

The number array should be 5,6,7 but was 1, 2, 3, 5, 8

This should succeed

The names array should be Justin, Tamba, Spencer

This should fail

The names array should be Tom, Todd, Trevor but was Justin, Tamba, Spencer

Checking Json Values in a Table

As a shortcut, if you're fine exposing JSONPath expressions directly into your Storyteller specification visualization, you can use this built in grammar as shown in this specification:

Simple Value Checks

Verifying Json Data

The json is {"color": "red", "order": "1"}
Check Values within the JSON Document
Json PathValue
$.colorred
$.order1
$.colorwrong but was red
$.orderwrong but was 1

Correct Assertion

The order should be 1

Incorrect Assertion

The order should be 3 but was 1

In the underlying markdown file, the usage of the Check Json Values grammar used above looks like this:

# Simple Value Checks

[SampleJson]
|> CheckJsonValues
    [table]
    |path   |returnValue|
    |$.color|red        |
    |$.order|1          |
    |$.color|wrong      |
    |$.order|wrong      |


Set Verification of Set Elements

JsonComparisonFixture also contains a way to utilize Storyteller's set verification capability within Json documents. Here's a sample grammar that uses this functionality:

public IGrammar CheckPeople()
{
    // "$.children" designates the JSONPath to the json array
    return VerifyChildElementSet("$.children").Titled("The people should be")
        .Compare(_ =>
        {
            // Checking values within the elements of the Json array
            // The first argument is a JSONPath relative to each element of the array
            // The second argument is necessary to give Storyteller a name for
            // the expected data
            _.Check<string>("$.first", "first");
            _.Check<string>("$.last", "last");
            _.Check<int>("$.age", "age");

            // Arrays are valid here, and you can customize the Cell with
            // the fluent interface shown below
            _.Check<int[]>("$.numbers", "numbers").Header("the numbers");
            _.Check<string>("$.address.city", "city");
        });
}

Which is going to look like a normal set verification table in the results of a specification:

Set Verification on Children Elements

Verifying Json Data

The people in the children collection are
firstlastagecitynumbers
HanSolo25San Diego1,3,5
LukeSkywalker20Tatooine2,4,6
LeiaOrgana20Alderaan3,2,1
The people should be
firstlastagethe numberscity
MatchedHanSolo251,3,5San Diego
MatchedLukeSkywalker202,4,6Tatooine
MatchedLeiaOrgana203,2,1Alderaan
The people should be
firstlastagethe numberscity
MissingDarthVader502,3,4Mustafar
MatchedLukeSkywalker202,4,6Tatooine
MissingLeiaOrgana301,2,3Dallas
ExtraHanSolo251, 3, 5San Diego
ExtraLeiaOrgana203, 2, 1Alderaan

Deep Assertions

To do a deep assertion of an element within the Json document to an expected Json structure, you can use the assertDeepJsonEquals() method to do a semantic comparison (using Newtonsoft.Json behind the scenes):

[FormatAs("Deep equals check of {name} and {age}")]
public void CompareChild(string name, int age)
{
    var dict = new Dictionary<string, string> {{"name", name}, {"age", age.ToString()}};

    var json = JsonConvert.SerializeObject(dict);
    assertDeepJsonEquals("child", json);
    
}

If the comparison fails, you'll see the actual and expected values in the specification results:

Deep Child Comparison

Verifying Json Data

The json is {"child": {"name": "Declan", "age": "3"}}
Deep equals check of Declan and 3
Deep equals check of Max and 13

Expected:

{ "name": "Max", "age": "13" }

but was:

{ "name": "Declan", "age": "3" }

Other Helpers

JsonComparisonFixture includes a couple utility methods and properties:

  • deserialize<T>(path) -- deserializes the Json object or value found at the JSONPath to the designated type T.
  • JsonSerializerSettings -- use this to control the Newtonsoft.Json serialization within the current Fixture
  • StoreJson(json) -- load the Json data you want to verify
  • JObject -- if you're familiar with Newtonsoft's DOM model, you can directly utilize the topmost JObject node for the current Json document