# Creating Custom Fluent Assertions

> Enhance the readability of your tests by customizing Fluent Assertions to your use cases!

- URL: https://blog.nimblepros.com/blogs/creating-custom-fluentassertions/
- Author: [Kyle McMaster](https://blog.nimblepros.com/authors/kyle/)
- Published: 2022-09-23
- Categories: [Software Development](https://blog.nimblepros.com/categories/software-development/)
- Publisher: [NimblePros](https://blog.nimblepros.com/)

---

[Fluent Assertions](https://fluentassertions.com/) is a library used to make C# test code more readable and expressive. This frees your tests from syntax heavy assertion statements and allows them to reveal their true intention. The library contains a great set of extensions for most assertion statements you would expect to find in common testing scenarios. It is also easily extensible as we will see here shortly. First, I'll introduce a few snippets so those unfamiliar with the library's purpose can understand its use and appeal.

```csharp
public void BarShouldNotBeNull()
{
  var service = new FooService();

  var bar = service.GetBar();

  Assert.NotNull(bar);
}
```

This is a fairly straightforward test but could be better! Ideally, our [test naming convention strategy](https://ardalis.com/unit-test-naming-convention/) combined with our assertion statements should read like a story and tell us what the intent of the test is. Let's take a look at a comparable implementation using Fluent Assertions.

```csharp
public void BarShouldNotBeNull()
{
  var service = new FooService();

  var bar = service.GetBar();

  bar.Should().NotBeNull();
}
```

Now our assertion portion of the test reads just like the behavior we would expect! This is a basic example but can be very powerful when customized to match the ubiquitous language of your domain.

## A Simple Example from the Ardalis.Result Library

One great way to encourage best practices as a library author is to ship with a set of assertions that can be used to test your library. This is also a great opportunity for the open source community to enrich the ecosystem around a particular library or framework. In the following example, we'll add extensions to the [Ardalis.Result](https://blog.nimblepros.com/blogs/getting-started-with-ardalis-result/) library that will allow us to test our result driven code in a more expressive way. Let's take a look at one of the tests for the [Map method](https://blog.nimblepros.com/blogs/transforming-results-with-the-map-method/).

```csharp
[Fact]
public void ShouldProduceSuccess()
{
  var initial = Result<int>.Success(123);
  
  var actual = initial.Map(val => val.ToString());

  actual.IsSuccess.Should().BeTrue();
}
```

This example starts with a successful Result containing an integer value of 123 and maps it to a successful Result with a string value of "123". The assertion statement is pretty basic and not very hard to read, but semantically what we are really concerned about is that the Result _should be successful_ and not that some boolean value is true. This pattern of asserting on a boolean flag makes for a good custom assertion statement. The first step to creating a custom assertion is to define a class that inherits from `ReferenceTypeAssertions<TSubject, TAssertions>` abstract class. This class will contain the extension methods that will be used to assert on the type we are extending. In this case, we are extending the `Result<T>` type so we will create a `ResultTAssertions` class as shown below.

```csharp
public class ResultTAssertions<T> : ReferenceTypeAssertions<Result<T>, ResultTAssertions<T>>
{
  public ResultTAssertions(Result<T> instance) : base(instance) { }

  protected override string Identifier => "Result<T>";

  // Assertions will go here
}
```

The `ReferenceTypeAssertions<TSubject, TAssertions>` class is a generic class that takes two type parameters. The first parameter is the type we are extending which is referred to as the `Subject` of the assertion. The second parameter is a sort of self reference to the type being defined that inherits from `ReferenceTypeAssertions<TSubject, TAssertions>`. The `instance` parameter is the instance of the type we are extending that we will be asserting on through the `Subject` property. We can then add assertion methods that check the conditions we would like to apply. The following example adds an `AndConstraint<ResultTAssertions<T>>` method that checks the `Status` of the `Result<T>` instance. The `AndConstraint` class is used to chain multiple assertions together. This `Execute.Assertion` statement is used to perform the actual assertion and will throw an exception if the `ForCondition` statement evaluates to false. The `Given` method allows us to capture the value of the `Subject` property. The `BecauseOf` and `FailWith` methods allow us to customize the output of the assertion failure message.

```csharp
public AndConstraint<ResultTAssertions<T>> BeSuccess(string because = "", params object[] becauseArgs)
{
  Execute.Assertion
    .BecauseOf(because, becauseArgs)
    .Given(() => Subject)
    .ForCondition(s => s.IsSuccess)
    .FailWith("Expected Result to be success but was failure");
  return new AndConstraint<ResultTAssertions<T>>(this);
}
```

Next, we need to define a `Should()` extension method to apply the `ResultTAssertions<T>` to `Result<T>`. The `Should()` method is used to create an instance of the `ResultTAssertions<T>` class passing in the extended `Result<T>` as the instance parameter of the `ResultTAssertions` constructor.

```csharp
public static class ResultTExtensions
{
  public static ResultTAssertions<T> Should<T>(this Result<T> instance) => new(instance);
}
```

Finally, we can put the pieces together in the updated test method.

```csharp
[Fact]
public void ShouldBeSuccess()
{
  var initial = Result<int>.Success(123);
  
  var actual = initial.Map(val => val.ToString());

  actual.Should().BeSuccess();
}
```

## Another Example Using Enums Representing Status

Fields that are implemented with enums, or even better [SmartEnums](https://ardalis.com/listing-strongly-typed-enum-options-in-c/), are great candidates for custom assertions since they often represent statuses in application logic. Typically, unit tests often assert on their values which correspond to a semantic meaning or use case in the application's domain. In the Ardalis.Result library, these values are usually referred to by the name of their value such as "Ok", "Not Found", "Unauthorized" rather than the underlying enum value of 0, 1, 2 etc. For the following test, we can create a custom assertion to Results with a `Status` of NotFound.

```csharp
[Fact]
public void ShouldBeNotFound()
{
  var result = Result<int>.NotFound();

  result.Status.Should().Be(ResultStatus.NotFound);
}
```

Similar to the previous example, we will create a `ResultStatusAssertions` class that inherits from `ReferenceTypeAssertions<ResultStatus, ResultStatusAssertions>` and add a `Should()` extension method to the `ResultStatus` enum to create a `ResultStatusAssertions` instance. These two classes are shown below.

```csharp
public static class ResultStatusExtensions
{
  public static ResultStatusAssertions Should(this ResultStatus instance) => new(instance);
}

public class ResultStatusAssertions: ReferenceTypeAssertions<ResultStatus, ResultStatusAssertions>
{
  public ResultStatusAssertions(ResultStatus instance) : base(instance) { }

  protected override string Identifier => "ResultStatus";

  /// <summary>
  /// Asserts a Result's Status is NotFound
  /// </summary>
  /// <param name="because"></param>
  /// <param name="becauseArgs"></param>
  /// <returns></returns>
  public AndConstraint<ResultStatusAssertions> BeNotFound(string because = "", params object[] becauseArgs)
  {
    Execute.Assertion
      .BecauseOf(because, becauseArgs)
      .Given(() => Subject)
      .ForCondition(status => status == ResultStatus.NotFound)
      .FailWith("Expected Result to have status {0} but has {1}", ResultStatus.NotFound, Subject);

    return new AndConstraint<ResultStatusAssertions>(this);
  }
}
```

Again, this allows us to refactor our test to use the custom assertion. This is a much more expressive way to assert on the status of a Result and can be implemented for all of the values of the `ResultStatus` enum.

```csharp
[Fact]
public void ShouldBeNotFound()
{
  var result = Result<int>.NotFound();

  result.Status.Should().BeNotFound();
}
```

## Conclusion

Hopefully this post has given you some ideas on how to create your own custom Fluent Assertions for your test suite. As demonstrated above, custom Fluent Assertions can be a great way to make your tests more expressive and easier to read. When combined with the naming conventions and use cases of a particular framework or library, these assertions can provide a great way to help developers write better tests.

## Resources

* [Getting Started With Ardalis.Result](https://blog.nimblepros.com/blogs/getting-started-with-ardalis-result/)
* [SmartEnums](https://ardalis.com/listing-strongly-typed-enum-options-in-c/)
* [Fluent Assertions](https://fluentassertions.com/)
