XUnit – Part 2: Value and Type Based Assertions in xUnit

In xUnit and many other testing frameworks, assertion is the mean that we conduct our test. In other word we assert an expectation that something is true about a piece of code. There are many different types of assertion in xUnit that we can use. Normally assertions are based on different types of object, but it can be also based on the type of action that occur. There are also certain rules of thumbs that helps us to write better more focused tests. In this post we’re going to have a look at assertions in xUnit. I’m going to go through different aspect of assertion in xUnit. I also describe some rules that can be followed to have better tests.

Assertions In xUnit

Asserts are the way that we test a result produce by running specific code. In this section we’re going to see some assertions based on their type. I divided the assertions into three types.

  1. Assertions that operate over a value.
  2. Those that check a type and its reference.
  3. Finally the ones that inspect an action and the things that happened around this action.

I’m going to go through the first and second part in this post. In my next post we’re going through the third type of assertions.

Value Based Assertions in xUnit

Contains/DoesNotContain

This type of assertions look to see if certain value or object contains another value. We can use this type of assertion on variety of types. Below you can see an example.

There are many other overload of this type of assertion that takes a comparer or a string comparison type etc.

Assertion True/False

This type of assertions check to see if the result of our check if true or false.

Matches/DoesNotMatch

This type of assertion receive regular expression and check to see it matches the a certain text.

StartsWith/EndsWith

Check to see if a string starts with or ends with a specific string.

Set Related Asserts

These assertions operates on sets. They check if a set is a sub set or a super set of another set. There also the assertions to check if a set is a proper sub set or super set of another set.

Other Assertions

For the most part these assertion methods are self-explanatory. There are various overload of these methods that take different types and option. As one example, the Range method also has a generic version where you pass anything you want along with a comparer.

Type Based Assertions in xUnit

These kind of assertions operate on the type of object. From it’s reference equality to actual types.

These method mostly are self-explanatory. For example the Same method check to see if two objects share a same reference.

There another method which is StrictEqual that might needs a little more attention. This method created because for some structs, if they don’t have an equality comparer, the compiler is going to do its own calculation. Here is an interesting post that goes into depth about this issue. I quote some part of it here.

The CLR authors tried their best to make the default implementations of Equals and GetHashCode for value types as efficient as possible. But there are a couple of reasons why they won’t be as efficient as a custom version written by hand (or generated by a compiler) for a specific type. Boxing allocation. The way the CLR is designed, every call to a member defined in System.ValueType or System.Enum types cause a boxing allocation (**). (**) Unless the method is a JIT intrinsic. For instance, in Core CLR 2.1 the JIT compiler knows about Enum.HasFlag and emits a very optimal code that causes no boxing allocations.

Potential collisions of the default GetHashCode implementation. An implementer of a hash function faces a dilemma: make a good distribution of the hash function or to make it fast. In some cases, it’s possible to achieve them both, but it is hard to do this generically in ValueType.GetHashCode. The canonical hash function of a struct “combines” hash codes of all the fields. But the only way to get a hash code of a field in a ValueType method is to use reflection. So, the CLR authors decided to trade speed over the distribution and the default GetHashCode version just returns a hash code of a first non-null field and “munges” it with a type id (***) (for more details see RegularGetValueTypeHashCode in coreclr repo at github).

How Many Assertions Per Unit Test?

There’s a rule that we should have one assertion per test. But it’s often misunderstood. The idea is that we have a test case, so whatever number of assertions we make should all be in line with testing that one test case. If we’re testing something else that is unrelated then it’s a problem. But as long as it’s testing the one case that we’re testing, it is OK to have multiple asserts that test the same case in different way. There are other opinions about this also, but this is what I think is the most practical and makes sense.

Tests Without An Assert

It is possible to write a test without using any assertion. The way it works is that if a method decorated with [Fact] and it does not throw any exception, then that test method passes. We usually see that type of tests as smoke tests. There are a lot of opinions about it, some people think it should be avoided. Other people say they can be useful as a smoke test. I’d go with the former opinion. The reason is that I think the framework gives us all kind of tools to write tests. I’d go with the way Brad Wilson thinks is best, that is using Record.

I think it is reasonable that when we use a testing framework for tests, we use that framework fully instead of relying on how the framework behave.

Summary

In this post we saw what assertion is and we also went through some of the available methods. I also introduced some best practice around this subject. I also created a repository containing all the example used in this post, you can find it here.

Share...
 

Hamid Mosalla

Hi, I'm Hamid Mosalla, I'm a software developer, indie cinema fan and a classical music aficionado. Here I write about my experiences mostly related to web development and .Net.

 

Leave a Reply

Your email address will not be published. Required fields are marked *