It has been way too long since I last released a new version of Fluent Assertions, but somehow my intention to deliver at least every three months has once again failed by the obligations a working husband and father of two has. Nonetheless, Fluent Assertions 2.1 is a fact. And although it isn't such a big release as 2.0, somehow it has accumulated a lot of nice improvements. As always you'll find the detailed release notes on the NuGet landing page, but just for the fun of it, let me provide some background on some of the changes 2.1 introduces.
For instance, the primary reason why this release took so long was the amount of work required to add the following two improvements to the structural equality assertions; reporting all differences and order independence. Those two required me to almost completely rewrite the internal engine. You may think "How difficult can that be?", but order independence between collections requires FA to compare each item from the subject collection with each item from the expected collection. Now consider that comparing two items might actually involve comparing two object graphs as well. If you find an exact match, all is fine. But what if there's no exact match? Which object graph should FA use for reporting the differences? I decided to solve this problem by selecting the object graph with the least amount of differences compared to the expectation. It will not always give you the perfect result (what if two items result in the exact same number of differences?), but chances are you'll get enough information to fix your code. As an example, consider the following scenario:
var subject = new
Property1 = "A",
Property2 = "B",
SubType1 = new
SubProperty1 = "C",
SubProperty2 = "D",
var expectation = new
Property1 = "1",
Property2 = "2",
SubType1 = new
SubProperty1 = "3",
SubProperty2 = "D",
Calling subject.ShouldBeEquivalentTo(expectation ) will result in the following test failure
Expected property Property1 to be "1", but "A" differs near "A" (index 0).
Expected property Property2 to be "2", but "B" differs near "B" (index 0).
Expected property SubType1.SubProperty1 to be "3", but "C" differs near "C" (index 0).
- Select all declared properties
- Match property by name (or throw)
- Invoke Action<DateTime> when info.RuntimeType.IsSameOrInherits(System.DateTime)
- Invoke Action<String> when info.RuntimeType.IsSameOrInherits(System.String)
Supporting aggregated exceptions was another of those little challenges. What I tried to accomplish is that the various ShouldThrow and ShouldNotThrow overloads would intercept any AggregateException instances and apply the assertion on the exceptions within. So from an end-user perspective it shouldn't matter if some expected or unexpected exception is first wrapped in an AggregateException. The less trivial part involved adding that behavior without breaking the .NET 3.5 and Silverlight versions (they share the same extension methods). Using the Strategy Pattern by means of an IExtractExceptions interface allowed me to plug in a framework-specific version of Fluent Assertions.
And while I was looking at exceptions anyway, I decided to change the way exception messages are asserted. In version 2.0 you had the option to specify how FA should interpret the WithMessage extension method. Having worked with this for a while in our own project (with 6000 unit tests) I came to the conclusion that you should really never want to check the exception message using an exact case-sensitive match. Doing that would only result in very brittle unit tests. As a result of this, I decided that as of version 2.1, the ComparisonMode is obsolete and any assertions against the exception message is treated as a case-insensitive wildcard match.
So what's next? Well, before including those little feature requests waiting on the issue list, I have two important steps to complete.
Move to GitHub
I've long hoped that Microsoft's approach embracing the open-source mindset would provide the CodePlex team with the resources to make it a first-class hub for open-source projects. Support for Git was a major step, but the lack of any big improvements for over a year is what made me decide to move to GitHub. The source code and binaries have already been moved to its new home, but I still need to clean up the documentation and find a definite place for downloading
Switch to Portable Class Libraries
Supporting multiple versions of FA has always been a pain, even while using linked source files. Especially during the many internal redesigns of 2.1, I got sick of constantly having to fix-up renames of classes or copy an added file into all other projects. I'm not 100% sure if PCLs will solve all problems, but I will give it a try anyhow.