Take care of your asserts
October 01, 2017It is highly probable that you have been in that kind of situation. You changed some code, run tests and some of them failed. You go there, look at the error message and see something like:
Expected: True
But was: False
I think we agree that is not very helpful. Fortunately, we can do much much better.
I will show you how to improve your asserts so that they have beautiful error messages and at the same time serve as documentation. We will see step by step how to go from the most basic asserts to more sophisticated ones.
Level 0
By level 0 I mean using only simplest asserts methods like Assert.True and Assert.False without any error message.
Assert.True(segments.SingleOrDefault(s => s.Archived == true));
When such assert fails developer can only imagine what the author intention was. One has to reverse engineer thought process to understand why that condition must to be true.
Level 1
The simplest improvement we can make to is to add an error message, that will describe why the condition must hold.
Assert.True(segments.SingleOrDefault(s => s.Archived == true), "after merge of segments one of them is marked as archived");
The message goes a long way of making test readable and maintainable.
Level 2
Next step is to use more specific assert to further improve the error message. Remember that the more specific the assert, the more information it provides. I will use FluentAssertions library for the task since it already has piles of useful assert methods.
segments.Should().ContainSingle(s => s.Archived == true, "after merge of segments one of them is marked as archived");
Level 3
Now, we will head towards more sophisticated refactoring. I have rarely seen it used by other developers. I am talking about extracting method with an assert and name it using domain language. Method name should abstractly describe what condition is verified using domain language, not the implementation language.
private void AssertThatThereIsOneArchivedSegment(params Segment[] segments)
{
segments.Should().ContainSingle(s => s.Archived == true, "after merge of segments one of them is marked as archived");
}
This simple maneuver makes the test even more readable and much more maintainable. Now, implementation refactorings only influence implementation of your helper assert method, the main test is intact.
In given example, I used a private method in the test class, but any other means of extraction, like some helper class for asserts is just as effective.
Summary
Making asserts in a way I presented is low hanging fruit just waiting to be consumed. Your tests will be more readable and flexible in case of refactorings. When they fail, they will provide useful information for maintainers.