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

Updated Wiki: Documentation

$
0
0

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

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

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

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

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 method. With version 1.4, you no longer have to use the TimeSpan class directly and instead use extension methods to convert a number to a TimeSpan.

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 ==

version 1.4 introduces 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();

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 with() method introduced in version 1.4.0:

action act = () => subject.foo(null));
act.shouldthrow<argumentnullexception>().where(e => e.message.startswith(“did”));

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

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

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.

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)
{ 
  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.

Viewing all articles
Browse latest Browse all 1402

Trending Articles



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