Challenge Your Favorites

November 13, 2019

I’ve written about the many benefits of personal projects before. One nice aspect is being able to try out tools & frameworks in a low-risk environment. In this post I’ll explain one such trial I’m doing and how the tool Comby made switching easy.

Are My Go-To Tools Stale?

As we gain experience on projects we naturally build up lists of tools that have worked out well for us in the past. One such tool for me is NUnit, my default unit testing framework for .NET.

However, just because I’ve successfully used it in the past doesn’t mean it’s still the best option today. On the one hand I don’t want to immediately change frameworks whenever a New Shiny™ thing arrives, but on the other hand I don’t want to be stubbornly using the same tools from sheer inertia.

Possible Unit Test Replacements

xUnit

xUnit makes a strong case for being the new “default” unit testing framework in the .NET space. Created by some of the folks who previously created NUnit 2, it was an attempt to make a more sleek, modern test framework with less special rules.

However, coming from NUnit, xUnit initially struck me as a little too minimal. Where’s all my fluent assertions? I liked those!

FluentAssertions

Enter the appropriately named FluentAssertions. Unlike xUnit or NUnit, this isn’t a stand-alone test framework. Instead, it focuses on making a better version of NUnit’s fluent assertion system:

// NUnitAssert.That(balance, Is.GreaterThan(5));// FluentAssertionsbalance.Should().BeGreaterThan(5);

This might at first seem like a trivial distinction, but the real magic is what happens when the assertion fails:

// NUnit failure outputExpected: greater than 5  But was:  2// FluentAssertions failure outputExpected balance to be greater than 5, but found 2.

See how FluentAssertions automatically included the name of the variable in the failure message? That is incredibly helpful in providing context when combing through the failure output from a build server, without having to constantly dig up the code. Of course you can include more information in the NUnit error message too, but you have to manually include it.

Isn’t Switching Painful?

OK, so the combination of xUnit and FluentAssertions seems neat enough to try out. However, won’t switching over be extremely tedious?

Some things, like changing the [Test] attribute to [Fact], can be easily converted with simple search/replace operations. Others are a bit thornier:

// This NUnit floating point assertion...Assert.That(frequency, Is.EqualTo(threshold * 5).Within(0.0001));// turns into this in FluentAssertionsfrequency.Should().BeApproximately(threshold * 5, 0.0001);

Could we write a regular expression to do this conversion? Yeah, perhaps, but it would be ugly. And what if the expected value was actually a function call, like ComputeThreshold(userEnteredLimit)? Those extra parenthesis would be extremely annoying to deal with!

Comby to the Rescue

Comby is modestly described as “a tool for changing code.” The niche it covers is when a regular expression isn’t good enough, but a full-blown parser is complete overkill:

Comby-Complexity-Spectrum

Comby is a command line tool that takes as input a match template, a condition for when to match, a rewrite template, and (optionally) the file extensions to look for. For the assertion we talked about, the relevant templates are:

Match Template:Assert.That(:[actual], Is.EqualTo(:[expected]).Within(:[threshold]));Rewrite Template::[actual].Should().BeApproximately(:[expected], :[threshold]);

The magic of Comby is that it takes care of annoying things like matching parentheses, dealing with strings, or ignoring comments – things that you have to manually take care of with regular expressions.

Reminder when I asked what would happen if the expected value was actually a function call? For Comby, it doesn’t care – the :[expected] “hole” (their terminology) will happily match 5, or threshold * 5, or f(g(h(5))), or… you get the picture. Plus, I hope we can all agree that the above templates are infinitely more readable than the equivalent regular expression would be.

Another cool thing is the live demo site – follow that link to see an interactive version of the above example! Another super nice feature is that the site will generate the console command to run at the click of a button, so you essentially have a nice GUI for the tool.

Comby supports a number of different languages – this mainly changes things like what it considers a comment, which delimiters should be matched, etc. Unfortunately as of writing there is no explicit C# support, but using C/C++ syntax seems to work fine. Just remember to change the file extension that it’s looking for!

Keep Challenging Your Assumptions

This post obviously only scratches the surface of xUnit, FluentAssertions, and Comby, but hopefully you’ve gotten a bit of a taste of each one. That main takeaway of this post is that it’s always valuable to periodically double-check your thinking to make sure you don’t get stuck in a rut. If your favorite framework still the best? Maybe, maybe not – give one of its competitors a try to see if it still holds up. Is changing over to a new way of doing things painful? Maybe it doesn’t have to be with tools like Comby available.