Welcome! Spanish articles on LinkedIn. You can follow me on Twitter for news.
xUnit
One of the most popular frameworks to test code in the .NET ecosystem is xUnit.
xUnit.net is a free, open-source, community-focused unit testing tool for .NET.
A common situation using xUnit
xUnit uses the Assert
class to verify conditions during the process of running tests. This class provides various extensions methods that commonly use two parameters:
- Expected value
- Actual value
Let's see an example.
var variable = 1;
Assert.Equal(10, variable);
Assert.Equal(variable, 10);
So, which one of these Assert.Equal
methods are correct? When we mix up the expected and the actual value parameters, if the test fails, the failure message may not make much sense.
In the first case, we get the correct message.
Assert.Equal() Failure
Expected: 10
Actual: 1
The second one is incorrect cause are expecting 10, not 1
Assert.Equal() Failure
Expected: 1
Actual: 10
Fluent Assertions
Fluent Assertions is a library that provides us:
- Clearer explanations about why a test failed
- Improve readability of test source code
Basically, with this library, we can read a test more like an English sentence.
If we perform the same test using Fluent Assertions library, the code will look something like this:
var variable = 1;
variable.Should().Be(10);
Let's take a look at the failure message.
Expected variable to be 10 but found 1.
This message is clearer than the Assert failure message. A more descriptive failure message may prevent the need for debugging through the test.
Let's dive into some examples
Inline asserts
We could write our asserts inline using the And
constraint of fluent assertions.
[Fact]
public void xUnit_StringTest()
{
string code = "001SUMMERCODE";
Assert.NotNull(code);
Assert.NotEmpty(code);
Assert.StartsWith("001", code);
Assert.EndsWith("code".ToUpper(), code);
}
[Fact]
public void xUnit_FluentAssertions_StringTest()
{
string code = "001SUMMERCODE";
//code.Should().NotBeNullOrEmpty();
//code.Should().StartWith("001");
//code.Should().EndWithEquivalent("code");
code.Should().NotBeNullOrEmpty().And.StartWith("001").And.EndWithEquivalent("code");
}
Custom messages
The because
parameter allows us to set a custom message when a test fails.
[Fact]
public void xUnit_FluentAssertions_CustomMessageTest()
{
string code = "002SUMMERCODE";
code.Should().NotBeNullOrEmpty().And.
StartWith("001", because: "the first batch of codes start with 001").And.
EndWithEquivalent("code");
}
Output
Expected code to start with
"001" because the first batch of codes start with 001, but
"002SUMMERCODE" differs near "2SU" (index 2).
Assertion scope
If we have multiple asserts and one fails, the next ones do not execute. This is the default behavior, but we can change it through the AssertionScope.
[Fact]
public void xUnit_FluentAssertions_StringTest()
{
string code = "001SUMMERCODE";
code.Should().NotBeNullOrEmpty();
code.Should().StartWith("002"); //fails
code.Should().EndWithEquivalent("code");
code.Should().ContainEquivalentOf("SUMMERS");//fails
}
Output
Expected code to start with
"002", but
"001SUMMERCODE" differs near "1SU" (index 2).
Using AssertionScope
[Fact]
public void xUnit_FluentAssertions_StringTest()
{
string code = "001SUMMERCODE";
using (new AssertionScope())
{
code.Should().NotBeNullOrEmpty();
code.Should().StartWith("002"); //fails
code.Should().EndWithEquivalent("code");
code.Should().ContainEquivalentOf("SUMMERS"); //fails
}
}
Output
Expected code to start with
"002", but
"001SUMMERCODE" differs near "1SU" (index 2).
Expected code to contain equivalent of
"SUMMERS" but found
"001SUMMERCODE".
The difference is that with AssertionScope,
we run all asserts.
Exceptions
The Throw
and ThrowExactly
methods help us to test if a method throws an exception. I recommend using ThrowExactly
because Throw
pass tests when check inheritance.
Let's define a Person
class.
public class Person
{
public string Name { get; }
public Person(string name)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
}
}
Then, test the constructor to throw the ArgumentNullException using Throw.
[Fact]
public void xUnit_FluentAssertions_ExceptionTest()
{
Action act = () => new Person(null);
act.Should().Throw<Exception>()
.WithMessage("Value cannot be null. (Parameter 'name')");
}
That's a successful test!
Switch to ThrowExactly
[Fact]
public void xUnit_FluentAssertions_ExceptionTest()
{
Action act = () => new Person(null);
act.Should().ThrowExactly<Exception>()
.WithMessage("Value cannot be null. (Parameter 'name')");
}
Output
Expected type to be System.Exception, but found System.ArgumentNullException.
End!
That was an introduction to this amazing library! I use a lot in the projects that I'm working on because of readability and easy use.
Top comments (1)
Thanks That was a short and clear introduction to Fluent Assertions using xUnit !