There are probably as many test styles as there are developers. And while diversity is a good thing, you most likely want your tests to have a consistent style.
Fluent style tests are ideal for creating structured, readable, and maintainable unit tests. This blog post will cover the implementation details.
What we will cover within this post:
- Setting up the prerequisites for our unit test project
- Creating a context helper (The ‘given’ of behavioral driven development)
- Create generic scenario extension methods
- A Sample implementation
Project Setup
The more that a test or the code reads like prose, the higher quality it is. I’ve explained this in greater detail within this post: “Better test productivity with fluent style tests.” However, this only covers the theory and not what this would look like in a c# unit test project.
To put this into practice, we will need to set up our unit test project. Note that I’m using NUnit, however you can use your favorite test framework. The setup is straightforward and only requires two NuGet packages to make our code natural-looking.
The first package is Fluent Assertions, which will help us writing, fluent assertions. And the second is call NSubstitute.
NSubstitute is an easy to use mocking library adhering the fluent style. Let’s go over some examples to understand why they are so important.
As you can see within the Example, the code has a natural flow, which is what we are after.
Fluent Assertions is a set of extension methods that allow you to communicate what your tests are trying to accomplish. Once again, its easier to explain with some examples.
Now that we have the prerequisites in place we can finally start writing some code.
Creating the context helper.
The context helper represents the initial preparation. This is also known as the Given part of GivenWhenThen convention from BDD. its a static class that contains static properties, each exposing classes used in the system under test. A property can return only one of the following types:
1 – An instance of a concrete class; This is only used for the object under test and should return the concrete type and not an interface. I also recommend the following naming convention AConcrete{name of you class}. The instance won’t be used but solely there in order to set up the test scenario using extension methods, this is covered later on.
2 – A mock object: Returns a NSubstitute mock based on a given interface.
Note: For smaller projects a single Given.cs file would do however you might want to split over functional areas when dealing with larger projects.
Creating the scenario helper extension methods.
The scenario helper extension methods are used to construct a sentence. Some examples Methods are When, Then, TheResult and others you might require or prefer. This is needed to create a natural flow, as seen in the example below. Without When() and Then().TheResult() the result is harder to understand.
Given.ConcreateObjectX.WhichIsSetupToXyz().CallMethodX().Should().BeNull();
Given.ConcreateObjectX.WhichIsSetupToXyz().When().CallMethodX().Then() .TheResult().Should().BeNull();
The methods are generic passthrough extension methods without any behavior.
Creating the scenario extension methods.
The scenario extension methods are used to configure your scenario. This includes the creation of the concrete object for the subject under test as well as the configuration of the mock objects. Given that they are all extension methods, you can easily compose your test setup by chaining multiple reusable methods.
Given.AnElectricCar.With().AFullBattery().And().SomeoneBehindTheWheel().When()...
Example test.
The test example below combines everything we have covered so far. I’m using BasedOnScenario
in order to inject the configured mocks.
I hope this information was helpful. There are still some advanced cases that haven’t been covered in this post. I will try to get this addressed in a follow-up article.
Recent Comments