Quantcast
Channel: Fluent Assertions
Viewing all articles
Browse latest Browse all 1402

Updated Wiki: Documentation

$
0
0

For the 1.7.1 release, click here.

Supported Test Frameworks

Fluent Assertions supports MSTest, NUnit, XUnit, MSpec, MBUnit and the Gallio Framework. 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. If it cannot find any of the supported frameworks, it will fall back to using a custom AssertFailedException exception class.

<configuration>    
  <appSettings>    
    <!-- Supported values: nunit, xunit, mstest, mspec, mbunit and gallio -->
    <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);

We’ve also added the possibility to assert that an object can be serialized and deserialized using the XML or binary formatters.

theObject.Should().BeXmlSerializable();
theObject.Should().BeBinarySerializable();

Nullable types

short? theShort = null;
theShort.Should().NotHaveValue();

int? theInt = 3;
theInt.Should().HaveValue();

DateTime? theDate = null;
theDate.Should().NotHaveValue();

Booleans

bool theBoolean = false;
theBoolean.Should().BeFalse("it's set to false");

theBoolean = true;
theBoolean.Should().BeTrue();
theBoolean.Should().Be(otherBoolean);

Strings

For asserting whether a string is null, empty or contains whitespace only, you have a wide range of methods to your disposal.

string theString = "";
theString.Should().NotBeNull();
theString.Should().BeNull();
theString.Should().BeEmpty(); theString.Should().NotBeEmpty("because the string is not empty"); theString.Should().HaveLength(0);
theString.Should().BeBlank(); // either null, empty or whitespace only
theString.Should().NotBeBlank();

Obviously you’ll find all the methods you would expect for string assertions.

theString = "This is a String";
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().NotContain("is a");
theString.Should().ContainEquivalentOf("WE DONT CARE ABOUT THE CASING");
theString.Should().NotContainEquivalentOf("HeRe ThE CaSiNg Is IgNoReD As WeLl"); theString.Should().StartWith("This"); theString.Should().StartWithEquivalent("this");

We even support wildcars. For instance, if you would like to assert that some email address is correct, use this:

emailAddress.Should().Match("*@*.com");

If the casing of the input string is irrelevant, use this:

emailAddress.Should().MatchEquivalentOf(*@*.COM);

Numeric types and everything else that implements IComparable<T>

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.Should().BeInRange(1,10); theInt = -8; theInt.Should().BeNegative(); double theDouble = 5.1; theDouble.Should().BeGreaterThan(5);
byte theByte = 2;
theByte.Should().Be(2);

Notice that Should().Be() and Should().NotBe() are not available for floats and doubles. As explained in this article, floating point variables are inheritably inaccurate and should never be compared for equality. Instead, either use the Should().BeInRange() method or the following method specifically designed for floating point variables.

float value = 3.1415927F;
value.Should().BeApproximately(3.14F, 0.001F);

This will verify that the value of the float is between 3.139 and 3.141.

Dates, times and time spans

For asserting a date and time against various constraints, FA offers a bunch of methods that, provided that you use the extension methods for representinging dates and times, really help to keep your assertions readable.

var theDatetime = 1.March(2010).At(22,15);
theDatetime.Should().BeAfter(1.February(2010)); theDatetime.Should().BeBefore(2.March(2010)); theDatetime.Should().BeOnOrAfter(1.March(2010)); theDatetime.Should().Be(1.March(2010).At(22, 15)); theDatetime.Should().HaveDay(1); theDatetime.Should().HaveMonth(3); theDatetime.Should().HaveYear(2010); theDatetime.Should().HaveHour(22); theDatetime.Should().HaveMinute(15); theDatetime.Should().HaveSecond(0);

We'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 method.

theDatetime.Should().BeLessThan(10.Minutes()).Before(otherDatetime); // Equivalent to <
theDatetime.Should().BeWithin(2.Hours()).After(otherDatetime);       // Equivalent to <=
theDatetime.Should().BeMoreThan(1.Days()).Before(deadline);          // Equivalent to >
theDatetime.Should().BeAtLeast(2.Days()).Before(deliveryDate);       // Equivalent to >=
theDatetime.Should().BeExactly(24.Hours()).Before(appointement);     // Equivalent to ==

Some dedicated methods that apply to TimeSpans directly:

var timespan = new Timespan(12, 59, 59); 
timespan.Should().BePositive(); 
timespan.Should().BeNegative(); 
timespan.Should().Be(12.Hours()); 
timespan.Should().NotBe(1.Days()); 
timespan.Should().BeLessThan(someOtherTimespan); 
timespan.Should().BeLessOrEqual(someOtherTimespan); 
timespan.Should().BeGreaterThan(someOtherTimespan); 
timespan.Should().BeGreaterOrEqual(someOtherTimespan);

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();

Dictionaries

You can apply Fluent Assertions to your generic dictionaries as well. Of course you can assert any dictionary to be null or not null, and empty or not empty. Like this:

dictionary.Should().BeNull();
dictionary.Should().NotBeNull();
dictionary.Should().BeEmpty();
dictionary.Should().NotBeEmpty();

You can also assert the equality of the entire dictionary, where the equality of the keys and values will be validated using their Equals implementation. Like this:

var dictionary1 = new Dictionary<int, string>
{
    { 1, "One" },
    { 2, "Two" }
};

var dictionary2 = new Dictionary<int, string>
{
    { 1, "One" },
    { 2, "Two" }
};

var dictionary3 = new Dictionary<int, string>
{
    { 3, "Three" },
};

dictionary1.Should().Equal(dictionary2);
dictionary1.Should().NotEqual(dictionary3);

Or you can assert that the dictionary contains a certain key or value:

dictionary.Should().ContainKey(1);
dictionary.Should().NotContainKey(9);
dictionary.Should().ContainValue("One");
dictionary.Should().NotContainValue("Nine");

You can also assert that the dictionary has a certain number of items:

dictionary.Should().HaveCount(2);

And finally you can assert that the dictionary contains a specific key/value pair or not:

KeyValuePair<int> string> item = new KeyValuePair<int> string>(1, "One");

dictionary.Should().Contain(item);
dictionary.Should().Contain(2, "Two");
dictionary.Should().NotContain(9, "Nine");

Guids

The assertions you can do on Guids are simple. You can assert their equality to another Guid, or you can assert that a Guid is empty.

Guid theGuid = Guid.NewGuid();
Guid sameGuid = theGuid;
Guid otherGuid = Guid.NewGuid();

theGuid.Should().Be(sameGuid);
theGuid.Should().NotBe(otherGuid);
theGuid.Should().NotBeEmpty();

Guid.Empty.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");

An alternative syntax for doing the same is by chaining or more calls to the Where() method introduced in version 1.4.0:

Action act = () => subject.Foo(null)); 
act.ShouldThrow<ArgumentNullException>().Where(e => e.Message.StartsWith(“did”));

However, we discovered that testing the exception message for a substring is so common, that we introduced some specialized methods for that.

Action act = () => subject.Foo(null)); 
act
  .ShouldThrow<ArgumentNullException>()
  .WithMessage(“did”, ComparisonMode.StartWith);

Supported ComparisonModes are: Exact (the default), Equivalent, StartWith, StartWithEquivalent, Substring, EquivalentSubstring and Wildcard. The latter can be used if you really want to get in the nitty gritty and allows you to compare the message with a wildcard expression.

Action act = () => subject.Foo(null)); 
act
  .ShouldThrow<ArgumentNullException>()
  .WithMessage(“?did*”, ComparisonMode.Wildcard);

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 and fits better to the AAA syntax. 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>();

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 iterate 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 and fits better to the AAA syntax.

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>();

Object graph comparison

Consider the class Order and its wire-transfer equivalent OrderDto (a so-called DTO). Suppose also that an order has one or more Products and an associated Customer. Coincidentally, the OrderDto will have one or more ProductDtos and a corresponding CustomerDto. Now if you want to make sure that all the properties of all the objects in the OrderDto object graph match the equally named properties of the Order object graph, you can do this.

orderDto.ShouldBeEquivalentTo(order);

In contrast to the ShouldHave() extension method, the comparison is recursive by default and all properties of the OrderDto must be available on the Order. If not, an exception is thrown. You can override this behavior in different ways. For instance, you may only want to include the properties both object graphs have:

orderDto.ShouldBeEquivalentTo(order, options =>
    options.ExcludingMissingProperties());

You can also exclude certain (potentially deeply nested) properties using the Excluding() method.

orderDto.ShouldBeEquivalentTo(order, options =>
    options.Excluding(o => o.Customer.Name));

Obviously, Excluding() and ExcludingMissingProperties() can be combined. Maybe farfetched, but you may even decide to exclude a property on a particular nested object by its index.

orderDto.ShouldBeEquivalentTo(order, options =>
    options.Excluding(o => o.Products[1].Status));

The Excluding() method on the options object also takes a lambda expression that offers a bit more flexibility for deciding what property to include.

orderDto.ShouldBeEquivalentTo(order, options => options

    .Excluding(ctx => ctx.PropertyPath == "Level.Level.Text"));

This expression has access to the property path, the property info and the subject’s run-time and compile-time type. You could also take a different approach and explicitly tell FA which properties to include.

orderDto.ShouldBeEquivalentTo(order, options => options
    .Including(o => o.OrderNumber)
    .Including(o => o.Date));


Overriding and collections

In addition to influencing the properties that are including in the comparison, you can also override the actual assertion operation that is executed on a particular property.

orderDto.ShouldBeEquivalentTo(order, options => options
    .Using<DateTime>(ctx => ctx.Date.Should().BeCloseTo(ctx.Date, 1000))
    .When(info => info.PropertyPath.EndsWith("Date")));

If you want to do this for all properties of a certain type, you can shorten the above call like this.

orderDto.ShouldBeEquivalentTo(order, options => options
    .Using<DateTime>(ctx => ctx.Date.Should().BeCloseTo(ctx.Date, 1000))
    .WhenTypeIs<DateTime>();

The original ShouldHave() extension method does support collections now, but it doesn’t allow you to influence the comparison based on the actual collection type. The new extension method ShouldAllBeEquivalentTo() does support that so you can now take the 2nd example from the post and apply it on a collection of OrderDtos.

orderDtos.ShouldAllBeEquivalentTo(orders, options =>   options.Excluding(o => o.Customer.Name));

Extensibility

Internally the comparison process consists of three phases.

  1. Select the properties of the subject object to include in the comparison.
  2. Find a matching property on the expectation object and decide what to do if it can’t find any.
  3. Select the appropriate assertion method for the property’s type and execute it.

Each of these phases is executed by one or more implementations of ISelectionRule, IMatchingRule and IAssertionRule that are maintained by the EquivalencyAssertionOptions. The ExcludePropertyByPredicateSelectionRule for example, is added to the collection of selection rules when you use the Excluding(property expression) method on the options parameter of ShouldBeEquivalentTo(). Even the Using().When() construct in the previous section is doing nothing more than inserting an AssertionRule<TSubject> in to the list of assertion rules. Creating your own rule is quite straightforward. 

  1. Choose the appropriate phase that the rule should influence
  2. Select the corresponding interface
  3. Create a class that implements this interface
  4. Add it to the ShouldBeEquivalentTo() call using the Using() method on the options parameters.

    subject.ShouldBeEquivalentTo(expected, options => options.Using(new ExcludeForeignKeysSelectionRule()))

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.

Additionally, you can take structural comparison a level further by including the IncludingNestedObjects property. This will instruct the comparison to compare all (collections of) complex types that the properties of the subject (in this example) refer to. By default, it will assert that the nested properties of the subject match the nested properties of the expected object. However, if you do specify SharedProperties, then it will only compare the equally named properties between the nested objects. For instance:

dto.ShouldHave().SharedProperties().IncludingNestedObjects.EqualTo(customer);

Event Monitoring

Version 1.3.0 introduced 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);

In version 1.4 you can also do the opposite; asserting that a particular event was not raised.

subject.ShouldNotRaisePropertyChangeFor(x => x.SomeProperty);

Or, if your project is .NET 3.5 or 4.0 based:

subject.ShouldNotRaise(“SomeOtherEvent”);

Important Limitation: Due to limitations in Silverlight, only the ShouldRaisePropertyChangeFor() and ShouldNotRaisePropertyChangeFor() methods are supported in the Silverlight version of Fluent Assertions.

Type, Method and property assertions

Recently, we have added a number of assertions on types and on methods and properties of types. These are rather technical assertions and, although we like our unit tests to read as functional specifications for the application, we still see a use for assertions on the members of a class. For example when you use policy injection on your classes and require its methods to be virtual. Forgetting to make a method virtual will avoid the policy injection mechanism from creating a proxy for it, but you will only notice the consequences at runtime. Therefore it can be useful to create a unit test that asserts such requirements on your classes. Some examples.

typeof(MyPresentationModel).Should().BeDecoratedWith<SomeAttribute>();

MethodInfo method = GetMethod();
method.Should().BeVirtual();

PropertyInfo property = GetSomeProperty();
property.Should().BeVirtual().And.BeDecoratedWith<SomeAttribute>();

You can also perform assertions on multiple methods or properties in a certain type by using the Methods() or Properties() extension methods and some optional filtering methods. Like this:

typeof(MyPresentationModel).Methods()
  .ThatArePublicOrInternal 
  .ThatReturnVoid
  .Should()
  .BeVirtual("because this is required to intercept exceptions");

typeof(MyController).Methods()
  .ThatReturn<ActionResult>()
  .ThatAreDecoratedWith<HttpPostAttribute>()
  .Should()
  .BeDecoratedWith<ValidateAntiForgeryTokenAttribute>(
"because all Actions with HttpPost require ValidateAntiForgeryToken");

You can even assert methods or properties from all types in an assembly that apply to certain filters, like this:

var types = typeof(ClassWithSomeAttribute).Assembly.Types()
  .ThatAreDecoratedWith<SomeAttribute>()
  .ThatImplement<ISomeInterface>()
  .ThatAreInNamespace("Internal.Main.Test");

var properties = types.Properties().ThatArePublicOrInternal;
properties.Should().BeVirtual();

XML classes

Fluent Assertions has support for assertions on several of the LINQ-to-XML classes:

xDocument.Should().HaveRoot("configuration");
xDocument.Should().HaveElement("settings");

xElement.Should().HaveAttribute("age", "36");
xElement.Should().HaveElement("address");

xAttribute.Should().HaveValue("Amsterdam");

Note that these assertions require you to reference the System.Xml and System.Xml.Linq assemblies.

Execution Time

New in version 1.4 is a method to assert that the execution time of particular method or action does not exceed a predefined value. To verify the execution time of a method, use the following syntax:

var subject = new SomePotentiallyVerySlowClass();
subject.ExecutionTimeOf(s => s.ExpensiveMethod()).ShouldNotExceed(500.Milliseconds());

Alternatively, to verify the execution time of an arbitrary action, use this syntax:

Action someAction = () => Thread.Sleep(510);
someAction.ExecutionTime().ShouldNotExceed(100.Milliseconds());

Since it doesn’t make sense to do something like that in Silverlight, it is only available in the .NET 3.5 and .NET 4.0 versions 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, string reason, params object[] reasonArgs)
{ 
    Execute.Verification
.ForCondition(somecondition)
.BecauseOf(reason, reasonArgs)
.FailWith("Expected object not to be {0}{reason}", null); }
  • Create a custom assertions class and use the Verification class to verify conditions and create comprehensive failure messages using the built-in formatters.

Viewing all articles
Browse latest Browse all 1402

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>