Verify: Snapshot Testing for C#
Verify is a snapshot tool that simplifies the assertion of complex data.
23rd Nov. 2020
When writing unit tests, asserting that a complex object is equal to what we expect can get arduous sometimes.
[Fact]
public void ToViewModel_ReturnsExpectedResult()
{
// Arrange
var senderId = Guid.NewGuid();
var recipientId = Guid.NewGuid();
var messageId = Guid.NewGuid();
var timestamp = new DateTime(2020, 11, 23, 16, 0, 0, 0);
var sender = new Person
{
Id = senderId,
FirstName = “John”,
LastName = “Smith”
};
var recipient = new Person
{
Id = recipientId,
FirstName = “Jane”,
LastName = “Smith”
};
var message = new Message
{
Id = messageId,
Timestamp = timestamp,
Text = “Fancy having pasta for dinner?”,
Sender = senderId,
Recipient = recipientId
};
var expected = new MessageViewModel
{
Id = messageId,
Timestamp = timestamp,
Text = “Fancy having pasta for dinner?”,
Sender = “John Smith”,
Recipient = “Jane Smith”
};
// Act
var actual = message.ToViewModel(sender, recipient);
// Assert
Assert.Equal(expected.Id, actual.Id);
Assert.Equal(expected.Timestamp, actual.Timestamp);
Assert.Equal(expected.Text, actual.Text);
Assert.Equal(expected.Sender, actual.Sender);
Assert.Equal(expected.Recipient, actual.Recipient);
}
The short example above will quickly get out of hand the more complex our resulting objects get and the more tests we add. There is also the possibility of forgetting to update assertions for any new properties that get added.
This is where Verify could come in. Much like Jest’s snapshot testing feature, Verify serialises the objects you pass and uses that initial output as the basis of future runs of tests.
[Fact]
public async Task ToViewModel_ReturnsExpectedResult_VerifyTests()
{
// …
// Assert
await Verifier.Verify(actual);
}
As you can see from the snippet, the assertion is much more terse. Verify instead generated a text file with the serialised values of actual
and it looks like this:
{
Id: Guid_1,
Sender: John Smith,
Recipient: Jane Smith,
Text: Fancy having pasta for dinner tonight?,
Timestamp: DateTime_1
}
The initial run of a unit test will always fail, this is because you will be comparing against a file that does not exist yet or is empty.
Extra Settings
Lets say you want to ensure that any new properties that get added will be used as part of the assertion, you can configure it like so.
[Fact]
public async Task ToViewModel_ReturnsExpectedResult_VerifyTests()
{
// …
// Assert
await Verifier.Verify(actual)
.AddExtraSettings(_ =>
{
// This will only apply to this test
_.DefaultValueHandling = DefaultValueHandling.Include;
});
}
Have a look at the possible Serializer settings here.
Conclusion
Verify allows us to focus on writing functionality rather than writing assertions. By leveraging techniques available to front-end unit tests, we can simplify assertions within out C# unit tests.
Sample code is available in this repo.