Comparing JSON strings in unit tests
What is the best way to assert a JSON string value in a unit test? You can compare them as strings, but for that to work you need to use consistent formatting that ensures the same data is always serialized into identical JSON, for example something like the JSON Canonical Form. But there are other options as well.
One way would be to deserialize the JSON string into its strongly typed object equivalent and compare the two object instances:
[Test]
public async Task ComparingDeserializedModelsDoesntDetectExtraFields()
{
var expectedJson = await LoadJson("TooManyFields.json");
var expectedModel = serializer.Deserialize(expectedJson);
var actualModel = new Model
{
Field1 = "1",
Field2 = "2",
Field3 = "3",
};
actualModel.Should().BeEquivalentTo(expectedModel);
}
Although FluentAssertions is excellent for comparing object graphs, the above test cannot detect if the expected JSON file contains additional fields, as in the following example:
{
"Field1": "1",
"Field2": "2",
"Field3": "3",
"Field4": "4"
}
A better choice is the FluentAssertions.Json extension. This library uses JToken
from Newtonsoft.Json as the basis for comparison. You do not need to worry if you use System.Text.Json for JSON serialization in your code. You can still use FluentAssertions.Json with Newtonsoft.Json in your tests to deserialize the actual and expected JSON strings before comparison:
[Test]
public async Task JsonAssertionsDetectTooManyFields()
{
var expectedJson = await LoadJson("TooManyFields.json");
var expected = JToken.Parse(expectedJson);
var actualModel = new Model
{
Field1 = "1",
Field2 = "2",
Field3 = "3",
};
var actualJson = serializer.Serialize(actualModel);
var actual = JToken.Parse(actualJson);
actual.Should().NotBeEquivalentTo(expected);
}
The above test correctly detects the extra field in the expected JSON string and clearly describes the difference in the error message:
JSON document misses property $.Field4.
However, there is one very important detail you need to keep in mind when using this library: Do not forget to import its namespace into the file:
using FluentAssertions.Json;
If you do, the code will compile, but the JSON files may be treated as identical, even though they are not. For example, this would happen if you use the following JSON file as input for the above test:
{
"Field1": "1",
"Field2": "2",
"Field4": "4"
}
To learn more, you can find more test cases in my GitHub repository.
If you need to write tests to validate the JSON files generated by your code, there are better ways than string comparison to detect differences. FluentAssertions.Json depends on JToken from Newtonsoft.Json to perform the comparison, and generates great error messages to describe the detected differences. Just make sure you do not forget to import the correct namespace when you use it.