NFluent Extensions

About

NFluent has been designed since the beginning to be easy to use and to provide a great user experience. That means that we want the API to be as Fluent as possible. And important part of the NFluent value is to allow a smooth writing of all the checks. That’s why we provided an important set of checks for all the base types.

But, in order to make it enjoyable for everyone we also provide a set of extensions that allows you to create extensions for your own types. we hope to provide with NFluent the core DSL for your tests and enhance the quality you can put in their writing.

Default Checks

Just to refresh you with the concepts of the NFluent Checks, let’s see some of the bases.

In order to provide a DSL for your tests, the Checks are based on the type of the SUT (System Under Test). Then, depending on the type you test, you’ll have a full fluent experience while writing with only the methods allowed on that type.

For example if you are testing an Integer:

var age = 21; 
Check.That(age).IsPositive(); 
Check.That(age).IsGreaterThan(18); 

Or Dates:

var einstein = new Date(1879,3,14); 
var anelka = new Date(1979,3,14); 
Check.That(einstein).IsBefore(anelka); 
Check.That(einstein).IsInSameMonthAs(anelka); 
//This one doesn't exist but it could;-) 
//Check.That(einstein).StopDoingStupidComparisonsJustBecauseOfMatchingDates(anelka) 

Or even better with lists :

var user = new User(); 
Check.That(user.Roles).ContainsExactly("guest","anonymous"); 

NFluent Extension

The default fluent checks provided in the core NFluent library are enough to replace you habitual Asserts. But Sometimes when you work with a real Domain (not anemic) and you want to provide a nice experience for the users of your library it is very valuable to create your own NFluent Checks!

This fit very well with recurrent tests you need to do.

All the secret of the NFluent extensions is on the ICheck<T> returned by the Check.That<T>(T sut) (among other things).

The idea is to provide an extension method on the ICheck<T> interface for the T type you want to check. That’s it! Don’t forget that it is this check type you have to extend and not the T type itself.

In fact, there is another secret to be able to extend you checks…In order to not pollute the intellisense experience, the value of the type you are checking is not provided on the ICheck<T> interface. That’s why it needs to be casted to a proper compatible type:

ICheck<mytype> mycheck = thecheck; 
var runnableCheck = mycheck as IRunnableCheck<mytype>; 
mytype myvalue = runnableCheck.Value; 
//test my Value.

This is now mostly internals and this cast is here only for information. Since v0.11 we have a nice helper to do that and this is how you must use it now:

void MyExtention(this ICheck<mytype> check) {

var runnableCheck = 
       ExtensibilityHelper<mytype>.ExtractRunnableCheck(check);

mytype myvalue = runnableCheck.Value; 

//test my Value and throw with a nice message if it is not what you expect
}

Once here you can use the Value and check what you need.

One other important point the Chaining part. If your extension is only a simple test, you may return nothing and just throw if you don’t have what you expect. On the other side, if you are building more complex things, it should be better to allow your Checks to be chainable. If you want to provide to the users of your extension an happy fluent syntax, you may want to be able to chain it with an other operator. In this case, you have to encapsulate your code inside a dedicated execute method in the runner. Then instead of the previous exemple, you may use:

ICheckLink<ICheck<mytype>>  MyExtention(this ICheck<mytype> check) {
 var runnableCheck = ExtensibilityHelper<mytype>.ExtractRunnableCheck(check);
 return runnableCheck.ExecuteCheck(
                () =>
                {
                    //do some test and throw if you're not happy
                },
                //add here a negated exception message for the NOT chaining);
}

Extend it now!

For example, imagine that you have in your application Users with Roles. For most of the business tests you need to do you’ll need to check that the user have a certain role.

Our User class for future usage:

public class User { 
   public int Id {get;set;} 
   public string Name {get;set;} 
   public IEnumerable<string> Roles {get;set;} 
   public User(int id, string name,IEnumerable<string> roles = null) { 
      Id = id; Name=name; 
      Roles = roles ?? new List<string>(); 
} } 

As you have many tests that include checking the roles existence of a user it is convenient to create a specific Check in order to factorize some code and to be more domain specific.

You just have to create a static class with your extension method on ICheck<User> like this:

public static class CheckUserExtensions 
{ 
   public static void HasRole(this ICheck<User> user, string role)
   {
      var runnableCheck = ExtensibilityHelper<User>.ExtractRunnableCheck(user);
      User value = runnableCheck.Value; 
      Check.That(value).IsNotEqualTo(null); 
      Check.That(value.Roles).IsNotEqualTo(null); 
      Check.That(value.Roles).Contains(role); 
   } 

Then, everywhere you need it instead of doing a lot of testings, you have a real domain word that makes sense:

[Test] 
public void should_do_something_with_users() 
{ 
   var user = new User(1,"rui",new []{"admin","editor"}); 
   Check.That(user).HasRole("test"); 
} 

Some conclusion

We saw some of the basics of NFluent and also how to create our own checks for our models.

NFluent by it’s nature, provides a really nice way to produce tests that make sense, that are easy to write (with dedicated intellisense for each type) and easy to read (it’s near plain english). Don’t forget that your tests should also be your documentation and providing fluently sentences near plain english instead of questionable asserts will enhance that.

If you are providing a library to other developers, it can also be very valuable to provide them a testing library with dedicated Checks for your models. It will enhance the understanding of the domain but also help them to write their own tests with the fluent interfaces you provided.

Happy Checking!

Posted in Articles, Technical Posts Tagged with: , ,
One comment on “NFluent Extensions
  1. Very nice! Look forward to test it very soon.

Leave a Reply to Laurent Kempé Cancel reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>