Source code checked in, #61658
Updated Release: Release 1.3.0 (Jan 14, 2011)
- Added a new set of extensions for asserting that events were properly raised.
- BeOfType<T>() and BeAssignableTo<T>() are available for all reference types.
- Added an OnlyContain() to the collection assertions that takes a lambda expression.
- Added an overload of someCollection.Should().Contain() that takes both an IEnumerable and a params array.
- Added support for subject.ShouldHave().SharedProperties().EqualTo(otherObject).
- Added support for an additional But(x => x.Property) method that can be combined with AllProperties() and SharedProperties()
Breaking Changes
- Changed the way test framework assemblies are found so that they are not relying on specific versions anymore.
- It will automatically find the framework assembly, but you can force it using an <appSetting> with name FluentAssertions.TestFramework
- Introduced separate versions for .NET 3.5 and .NET 4.0, both supporting all test frameworks.
- Switched to Silverlight 4 for the Silverlight version of Fluent Assertions.
- Renamed the Assertions base-class to ReferenceTypeAssertions.
- BeOfType<T>() was not properly checking that the subject is of the exact same type and not a subclass.
Improvements/Bugs
- Signed all assemblies with a strong name.
- Added some missing XML comments.
- Improved the extensibility by exposing the subject-under-test via a public Subject property and making all contructors protected.
- If the exception message is shorter than the expected one, but starts with the same text, it was not detected as a difference.
- Fixed a NullReferenceException when calling ObjectAssertions().Be() on a null object.
- As<T>() uses a type safe-cast now so that you can do stuff like someObject.As<SomeType>().ShouldNotBeNull();
- Fixed a bug that caused the property comparison assertions to miss internal or base-class properties.
Updated Wiki: Documentation
Supported Test Frameworks
Fluent Assertions supports MSTest, NUnit and XUnit. Starting with version 1.3.0, you can simply add a reference to the corresponding test framework assembly to the unit test project. Fluent Assertions will automatically find the corresponding assembly and use it for throwing the framework-specific exceptions. If, for some unknown reason, Fluent Assertions fails to find the assembly, try specifying the framework explicitly using a configuration setting in the project’s app.config.
<configuration> <appSettings> <!-- Supported values: nunit, xunit and mstest --> <add key="FluentAssertions.TestFramework" value="nunit"/> </appSettings> </configuration>
Reference Types
object theObject = null; theObject.Should().BeNull("because the value is null"); theObject = "whatever"; theObject.Should().BeOfType<String>("because a {0} is set", typeof(String)); theObject.Should().NotBeNull(); string otherObject = "whatever"; theObject.Should().Be(otherObject, "because they have the same values"); theObject = otherObject; theObject.Should().BeSameAs(otherObject); theObject.Should().NotBeSameAs(otherObject); var ex = new ArgumentException(); ex.Should().BeAssignableTo<Exception>("because it is an exception"); var dummy = new Object(); dummy.Should().Match(d => (d.ToString() == "System.Object")); dummy.Should().Match<string>(d => (d == "System.Object")); dummy.Should().Match((string d) => (d == "System.Object"));
Some users requested the ability to easily downcast an object to one of its derived classes in a fluent way.
customer.Animals.First().As<Human>().Height.Should().Be(178);
Booleans
bool theBoolean = false; theBoolean.Should().BeFalse("it's set to false"); theBoolean = true; theBoolean.Should().BeTrue(); theBoolean.Should().Be(otherBoolean);
Strings
string theString = ""; theString.Should().BeEmpty(); theString.Should().NotBeEmpty("because the string is not empty"); theString.Should().HaveLength(0); theString = "This is a String"; theString.Should().NotBeNull(); theString.Should().Be("This is a String"); theString.Should().NotBe("This is another String"); theString.Should().BeEquivalentTo("THIS IS A STRING"); theString.Should().EndWith("a String"); theString.Should().EndWithEquivalent("a string"); theString.Should().Contain("is a"); theString.Should().StartWith("This"); theString.Should().StartWithEquivalent("this"); string theString = null; theString.Should().BeNull();
Integers, singles, doubles, decimals and everything else that implements IComparable
int theInt = 5; theInt.Should().BeGreaterOrEqualTo(5); theInt.Should().BeGreaterOrEqualTo(3); theInt.Should().BeGreaterThan(4); theInt.Should().BeLessOrEqualTo(5); theInt.Should().BeLessThan(6); theInt.Should().BePositive(); theInt.Should().Be(5); theInt.Should().NotBe(10); theInt = -8; theInt.Should().BeNegative(); double theDouble = 5.1; theDouble.Should().BeGreaterThan(5); int? theInt = null; theInt.Should().NotHaveValue(); int? theInt = 3; theInt.Should().HaveValue();
Dates, times and time spans
var theDateTime = new DateTime(2010, 3, 1, 22, 15, 0); theDateTime.Should().BeAfter(new DateTime(2010, 2, 1)); theDateTime.Should().BeBefore(new DateTime(2010, 3, 2)); theDateTime.Should().BeOnOrAfter(new DateTime(2010, 3, 1)); theDateTime.Should().Be(new DateTime(2010, 3, 1, 22, 15, 0)); theDateTime.Should().HaveDay(1); theDateTime.Should().HaveMonth(3); theDateTime.Should().HaveYear(2010); theDateTime.Should().HaveHour(22); theDateTime.Should().HaveMinute(15); theDateTime.Should().HaveSecond(0);
In version 1.2 I've added a whole set of methods for asserting that the difference between two DateTime objects match a certain time frame. All five methods support a Before and After extension.
theDateTime.Should().BeLessThan(TimeSpan.FromMinutes(10).Before(otherDateTime); // Equivalent to < theDateTime.Should().BeWithin(TimeSpan.FromHours(2).After(otherDateTime); // Equivalent to <= theDateTime.Should().BeMoreThan(TimeSpan.FromDays(1).Before(deadLine); // Equivalent to > theDateTime.Should().BeAtLeast(TimeSpan.FromDays(2).Before(deliveryDate); // Equivalent to >= theDateTime.Should().BeExactly(TimeSpan.FromHours(24).Before(appointement); // Equivalent to ==
Collections
IEnumerable collection = new[] { 1, 2, 5, 8 }; collection.Should().NotBeEmpty() .And.HaveCount(4) .And.ContainInOrder(new[] { 2, 5 }) .And.ContainItemsAssignableTo<int>(); collection.Should().Equal(new List<int> { 1, 2, 5, 8 }); collection.Should().Equal(1, 2, 5, 8); collection.Should().BeEquivalent(8, 2, 1, 5); collection.Should().NotBeEquivalent(8, 2, 3, 5); collection.Should().HaveCount(c => c > 3).And.OnlyHaveUniqueItems(); collection.Should().HaveSameCount(new[] {6, 2, 0, 5}); collection.Should().BeSubsetOf(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, });
collection.Should().Contain(8).And.HaveElementAt(2, 5).And.NotBeSubsetOf(new[] {11, 56});
collection.Should().Contain(x => x > 3);
collection.Should().Contain(collection, 5, 6); // It should contain the original items, plus 5 and 6. collection.Should().OnlyContain(x => x < 10); collection.Should().OnlyContainItemsOfType<int>(); collection.Should().NotContain(82); collection.Should().NotContainNulls();collection.Should().NotContain(x => x > 10); collection = new int[0]; collection.Should().BeEmpty();
Exceptions
The following example verifies that the Foo() method throws an InvalidOperationException which Message property has a specific value.
subject.Invoking(y => y.Foo("Hello")) .ShouldThrow<InvalidOperationException>() .WithMessage("Hello is not allowed at this moment);
But if you, like me, prefer the Arrange-Act-Assert syntax, you can also use an Action in your act part.
Action act = () => subject.Foo2("Hello"); act.ShouldThrow<InvalidOperationException>() .WithInnerException<ArgumentException>() .WithInnerMessage("whatever");
Notice that the example also verifies that the exception has a particular inner exception with a specific message. In fact, you can even check the individual properties of the exception instance using the And property.
Action act = () => subject.Foo(null)); act.ShouldThrow<ArgumentNullException>() .And.ParamName.Should().Equal("message");
If the method you are testing returns an IEnumerable or IEnumerable<T> and it uses the yield keyword to construct that collection, just calling the method will not cause the effect you expected. Because the real work is not done until you actually iteratie over that collection. You can use the Enumerating() extension method to force enumerating the collection like this.
Func<IEnumerable<char>> func = () => obj.SomeMethodThatUsesYield("blah"); func.Enumerating().ShouldThrow<ArgumentException>();
You do have to use the Func<T> type instead of Action<T> then.
On the other hand, you may want to verify that no exceptions were thrown.
Action act = () => subject.Foo("hello"));
act.ShouldNotThrow();
I know that a unit test will fail anyhow if an exception was thrown, but this syntax returns a clearer description of the exception that was thrown.
If you want to verify that a specific exception is not thrown, and want to ignore others, you can do that using an overload:
Action act = () => subject.Foo("hello"));act.ShouldNotThrow<InvalidOperationException>();
Property Comparison
You can assert the equality of entire objects by comparing their properties by name. This even works if the types of the properties differ but a built-in conversion exists (through the Convert class). As an example, consider a Customer entity from some arbitrary domain model and its DTO counterpart CustomerDto. You can assert that the DTO has the same values as the entity using this syntax:
dto.ShouldHave().AllProperties().EqualTo(customer);
As long as all properties of dto are also available on customer, and their value is equal or convertible, the assertion succeeds. You can, however, exclude a specific property using a property expression, such as for instance the ID property:
dto.ShouldHave().AllPropertiesBut(d => d.Id).EqualTo(customer);
Which is equivalent to:
dto.ShouldHave().AllProperties().But(d => d.Id).EqualTo(customer);
The other way around is also possible. So if you only want to include two specific properties, use this syntax.
dto.ShouldHave().Properties(d => d.Name, d => d.Address).EqualTo(customer);
And finally, if you only want to compare the properties that both objects have, you can use the SharedProperties() method like this:
dto.ShouldHave().SharedProperties().EqualTo(customer);
Obviously, you can chain that with a But() method to exclude some of the shared properties.
Event Monitoring
Version 1.3.0 introduces a new set of extensions that allow you to verify that an object raised a particular event. Before you can invoke the assertion extensions, you must first tell Fluent Assertions that you want to monitor the object:
var subject = new EditCustomerViewModel();
subject.MonitorEvents();
Assuming that we’re dealing with a MVVM implementation, you might want to verify that it raised its
PropertyChanged event for a particular property:
subject .ShouldRaise("PropertyChanged")
.WithSender(subject) .WithArgs<PropertyChangedEventArgs>(args => args.PropertyName == "SomeProperty");
Notice that WithSender() verifies that all occurrences had its sender argument set to the specified object. WithArgs() just verifies that at least one occurrence had a matching EventArgs object. In other words, event monitoring only works for events that comply with the standard two-argument sender/args .NET pattern.
Since verifying for PropertyChanged events is so common, I’ve included a specialized shortcut to the example above:
subject.ShouldRaisePropertyChangeFor(x => x.SomeProperty);
Important Limitation: Due to limitations in Silverlight, only the ShouldRaisePropertyChangeFor() extension method is supported in the Silverlight version of Fluent Assertions.
Extensibility
Adding your own assertion extensions is quite straightforward and happens in my projects quite often. You have a few options though.
- Extend one of the built-in classes such as CollectionAssertions<T>or ReferenceTypeAssertions<T> and expose them through a custom static class with extension methods named Should().
- Create extension methods that extend an assertion class:
public static void BeWhatever<T>(this GenericCollectionAssertions<T> assertions) { if (assertions.Subject != ...) { Verification.Fail("Expected whatever..."); } }
- Create a custom assertions class and use the Verification class to verify conditions and create comprehensive failure messages using the built-in formatters.
Released: Release 1.3.0 (Jan 14, 2011)
- Added a new set of extensions for asserting that events were properly raised (has a limitation in Silverlight).
- BeOfType<T>() and BeAssignableTo<T>() are available for all reference types.
- Added an OnlyContain() to the collection assertions that takes a lambda expression.
- Added an overload of someCollection.Should().Contain() that takes both an IEnumerable and a params array.
- Added support for subject.ShouldHave().SharedProperties().EqualTo(otherObject).
- Added support for an additional But(x => x.Property) method that can be combined with AllProperties() and SharedProperties()
Breaking Changes
- Changed the way test framework assemblies are found so that they are not relying on specific versions anymore.
- It will automatically find the framework assembly, but you can force it using an <appSetting> with name FluentAssertions.TestFramework
- Introduced separate versions for .NET 3.5 and .NET 4.0, both supporting all test frameworks.
- Switched to Silverlight 4 for the Silverlight version of Fluent Assertions.
- Renamed the Assertions base-class to ReferenceTypeAssertions.
- BeOfType<T>() was not properly checking that the subject is of the exact same type and not a subclass.
Improvements/Bugs
- Signed all assemblies with a strong name.
- Added some missing XML comments.
- Improved the extensibility by exposing the subject-under-test via a public Subject property and making all contructors protected.
- If the exception message is shorter than the expected one, but starts with the same text, it was not detected as a difference.
- Fixed a NullReferenceException when calling ObjectAssertions().Be() on a null object.
- As<T>() uses a type safe-cast now so that you can do stuff like someObject.As<SomeType>().ShouldNotBeNull();
- Fixed a bug that caused the property comparison assertions to miss internal or base-class properties.
Updated Release: Release 1.3.0 (Jan 14, 2011)
- Added a new set of extensions for asserting that events were properly raised (has a limitation in Silverlight).
- BeOfType<T>() and BeAssignableTo<T>() are available for all reference types.
- Added an OnlyContain() to the collection assertions that takes a lambda expression.
- Added an overload of someCollection.Should().Contain() that takes both an IEnumerable and a params array.
- Added support for subject.ShouldHave().SharedProperties().EqualTo(otherObject).
- Added support for an additional But(x => x.Property) method that can be combined with AllProperties() and SharedProperties()
Breaking Changes
- Changed the way test framework assemblies are found so that they are not relying on specific versions anymore.
- It will automatically find the framework assembly, but you can force it using an <appSetting> with name FluentAssertions.TestFramework
- Introduced separate versions for .NET 3.5 and .NET 4.0, both supporting all test frameworks.
- Switched to Silverlight 4 for the Silverlight version of Fluent Assertions.
- Renamed the Assertions base-class to ReferenceTypeAssertions.
- BeOfType<T>() was not properly checking that the subject is of the exact same type and not a subclass.
Improvements/Bugs
- Signed all assemblies with a strong name.
- Added some missing XML comments.
- Improved the extensibility by exposing the subject-under-test via a public Subject property and making all contructors protected.
- If the exception message is shorter than the expected one, but starts with the same text, it was not detected as a difference.
- Fixed a NullReferenceException when calling ObjectAssertions().Be() on a null object.
- As<T>() uses a type safe-cast now so that you can do stuff like someObject.As<SomeType>().ShouldNotBeNull();
- Fixed a bug that caused the property comparison assertions to miss internal or base-class properties.
Source code checked in, #61690
Updated Wiki: Home
Project Description
Fluent Assertions is a set of .NET extension methods that allow you to more naturally specify the expected outcome of a TDD or BDD-style test. We currently use it in all our internal and client projects, and it is even used in the NCQRS project.
Why another framework?
We primarily use Visual Studio 2010’s own testing framework and were not satisfied by other similar frameworks. The best one we ran into missed a nice natural way for specifying the reason that is displayed when an assertion failed. Moreover, we like to be able to easily add domain-specific assertions without having to subclass a whole bunch of obscure interfaces and abstract classes. In the beginning of 2010, after having used the framework internally for almost a year, we decided to make it public and rebrand it as Fluent Assertions.
Example
// Verifying that a string begins, ends and contains a particular phrase. string actual = "ABCDEFGHI"; actual.Should().StartWith("AB").And.EndWith("HI").And.Contain("EF").And.HaveLength(9); // Verifying that the collection contains a specified number of elements
// and that all elements match a predicate.
IEnumerable collection = new[] { 1, 2, 3 }; collection.Should().HaveCount(4, "because we thought we put three items in the collection"))
collection.Should().Contain(i => i > 0); // Verifying that a particular business rule is enforced using exceptions. var recipe = new RecipeBuilder() .With(new IngredientBuilder().For("Milk").WithQuantity(200, Unit.Milliliters)) .Build(); Action action = () => recipe.AddIngredient("Milk", 100, Unit.Spoon); action .ShouldThrow<RuleViolationException>() .WithMessage("Cannot change the unit of an existing ingredient") .And.Violations.Should().Contain(BusinessRule.CannotChangeIngredientQuanity);
"Expected <4> items because we thought we put three items in the collection, but found <3>."
This should keep you from having to start the debugger to figure out what went wrong. This is one of the fundamental principles we think Fluent Assertions should help you with. Note that you don't need to include the word because explicitly. The framework will prepend your phrase with it automatically.
News
Fluent Assertions 1.2.3 has been released. It's another small release with some minor additions and bug fixes.
Fluent Assertions 1.2.2 has been released. It's a small release to fix some issues. Read more about it here
May 12th, 2010
Small release to fix an issue with enumerables that use the yield keyword. Now includes separate assemblies for different unit testing frameworks, including NUnit 2.5.5.10112.
April 12th, 2010
Fluent Assertions 1.2 has been released. Read more about it here
March 5th, 2010
We've worked hard to add some important missing features that we really needed, and also improve resilience against illegal arguments such as an empty collection or null. You can find it here: Fluent Assertions release 1.1.
Who are we?
- Dennis Doomen
- Koen Willemse
- Martin Opdam
Created Issue: Assertion on byte values [10173]
Due the missing overloads with a byte argument of the Should() method, assertions on values of type byte would almost fail because they are treated as assertions on objects.
It would be nice if those overloads for byte values could be integrated.
I will also upload a patch that contains the required changes.
Thanks
Marco
Patch Uploaded: #8253
marcoerni has uploaded a patch.
Description:
Fix for WorkItem 10173 (Assertions on byte values)
New Post: ShouldNotRaise() on EventAssertions
Hi
It would be nice if we could assert on events that a particular event was not raised.
I will upload a patch containg the required changes (including specs).
Kindly regards
Marco
Patch Uploaded: #8263
marcoerni has uploaded a patch.
Description:
Patch for extension on EventAssertions for ShouldNotRaise()
New Post: ShouldNotRaise() on EventAssertions
Hi Marco, sorry for not getting back to you earlier. I was on a skiiing trip. I'll check out your patches. Nevertheless, thanks for your effort up to now.
Source code checked in, #62357
Commented Issue: Assertion on byte values [10173]
Due the missing overloads with a byte argument of the Should() method, assertions on values of type byte would almost fail because they are treated as assertions on objects.
It would be nice if those overloads for byte values could be integrated.
I will also upload a patch that contains the required changes.
Thanks
Marco
Comments: Associated with changeset 62357.
Patch Applied: #8253
dennisdoomen has applied patch #8253.
Comment:
See corresponding work item
Source code checked in, #62358
Closed Issue: Better readability with extension methods on int [10087]
For example:
public static TimeSpan Minutes(this int source)
{
return TimeSpan.FromMinutes(source);
}
This allows assertions like:
theDateTime.Should().BeLessThan(10.Minutes().Before(otherDateTime));
Comments: Resolved with changeset 62358.
Reopened Issue: Better readability with extension methods on int [10087]
For example:
public static TimeSpan Minutes(this int source)
{
return TimeSpan.FromMinutes(source);
}
This allows assertions like:
theDateTime.Should().BeLessThan(10.Minutes().Before(otherDateTime));
Source code checked in, #62359
Patch Applied: #8263
dennisdoomen has applied patch #8263.
Comment:
Applied it and refactored the Silverlight and .NET versions to remove any duplication.