We all know how hard it is to write understandable and maintainable tests for our code. It's always nice to have a little help from a testing framework. ChillBDD is a tool that helps you structure your tests to follow BDD practices.
Last week I finished up on my book and figured I've got time on my hands to do software development for a change. I'm in the middle of writing an ASP.NET Core website that might replace the ghost blog that I'm running right now. As part of my efforts to write this website I decided to give ChillBDD a shot. Here's how it went.
What is Chill?
Chill is a testing framework that is aimed at supporting behavior driven development. In behavior driven development (BDD) you write automated specifications that define the behavior of your code. You then write your regular production code to implement this behavior.
Tests that use a BDD approach have a distinct style:
public class WhenSomethingHappensToMyTestSubject
{
public WhenSomethingHappensToMyTestSubject()
{
// Given
// When
}
[Fact]
public void ThenSomeFactIsTrue()
{
// Your assertion
}
}
Typically you write a test class per scenario that you want to test. In this scenario you provide a given state for the test and execute some behavior on the component your testing. You then use separate test methods to verify the behavior. Typically, you'll use a single assertion per test method.
Frameworks like XUnit already make it pretty straight forward to follow this style of automated testing. But they are pretty free format. You can move away from the BDD style pretty easily.
Don't get me wrong, there's nothing wrong with moving away from BDD once in a while if your code calls for it. But I also feel that having a free style testing framework doesn't help you if you want to learn how to properly use BDD style tests.
Chill approach is different in that it forces you to use BDD style testing code. It has a set of helper methods and a base class that make it easier to use BDD style testing.
Writing a Chill test
To use Chill you need to add the Chill
package to your .NET test project. As far as I know it only supports using XUnit, so be sure to use that test framework as the basis for your test project.
I used the following set of commands to quickly generate a test project with Chill:
dotnet new xunit -o test/ApplicationTests
dotnet add test/ApplicationTests package Chill
Once you have a test project you can create a new test class using the GivenWhenThen base class that comes with Chill:
using Chill;
using Xunit;
public class WhenADraftPostIsCreated: GivenWhenThen<Post>
{
public WhenSomethingHappensToMyTestSubject()
{
Given(() =>
{
// Configure the state
});
When(() => Post.CreateDraft("Some title", "Some description"));
}
[Fact]
public void ThenThePostHasATitle()
{
Assert.Equal("Some title", Result.Title);
}
}
Working with a test subject
The GivenWhenThen
base class doesn't specify a test subject. This kind of test is great to test things like factory methods and static functions. Most of us will be writing test logic for object oriented programs though.
This is where the GivenSubject
base class comes in.
using Chill;
using Xunit;
public class WhenADraftPostIsCreated: GivenSubject<PostManager>
{
public WhenSomethingHappensToMyTestSubject()
{
Given(() =>
{
});
When(() => Subject.Create("Some title"));
}
[Fact]
public void ThenThePostHasATitle()
{
Assert.Equal("Some title", Result.Title);
}
}
With the GivenSubject
base class you can create a test that has a subject. The Chill framework automatically creates the subject using the default constructor so you don't have to worry about it.
Now you may be wondering, what about objects without a default constructor? You can create those to by adding an additional instruction to the test:
using Chill;
using Xunit;
public class WhenADraftPostIsCreated: GivenSubject<PostManager>
{
public WhenSomethingHappensToMyTestSubject()
{
Given(() =>
{
WithSubject(provider => PostManagerFactory.Create());
});
When(() => Subject.Create("Some title"));
}
// ...
}
Using the method WithSubject
you can control how your test subject is created. I've used this quite extensively when testing objects that needed to be constructed in a particular state.
Working with dependencies
Often times you'll need to inject dependencies into to your test subject. You'll usually use a mock version of your dependencies because that enables you to control the situation a bit better.
Chill supports the use of mocks through its UseThe
function. You can use this in your Given
lambda to provide a stunt-double for a dependency.
using Chill;
using Xunit;
using NSubstitute;
public class WhenADraftPostIsCreated: GivenSubject<PostManager>
{
public WhenSomethingHappensToMyTestSubject()
{
Given(() =>
{
UseThe(Substitute.For<IPostRepository>());
The<IPostRepository>()
.InsertAsync(Arg.Any<Post>)
.Returns(new Post());
});
When(() => Subject.Create("Some title"));
}
// ...
}
I'm using NSubstitute
here because it interoperates well with Chill. But you're free to use any mocking framework you like.
Note that you can access any components that you configured with UseThe
in your test using the The
method. It returns the object you want so you can, for example, set up a mock call.
Give this one a try!
I've been using Chill for a week now and I feel it has helped me structure my tests a little bit better. It's highly recommended if you haven't got much experience in programming yet. But it's also a great tool for experienced developers.
Give it a shot! You can check out sample code using Chill here: https://github.com/wmeints/fizzylogic/