Using FluentAssertions with xUnit Theory to Test for an Exception AND a Successful Return
2020-04-15 19:13
I recently wanted to unit test a method that required significant setup, and where an invalid method argument would throw an exception while valid values returned easily testable results.
While I could have created a separate test, this really lent itself to using an xUnit Theory. The problem I faced was how to test for the exception but also test for a valid return. I didn't want to write duplicate code within the test itself, such as declaring the service twice.
Some research and experimentation led to the approach below. The trick is to declare a delegate function, then use FluentAssertions to either catch the invocation exception, or to invoke the function and return the value.
Here's a simple Class Library app to demonstrate the technique. I kind of love this because there's no wasted or duplicate code.
using System;
using FluentAssertions;
using Xunit;
namespace FluentAssertionsInvoking
{
public class CustomerService_Should
{
[Theory]
[InlineData(null)]
[InlineData("Marcus Welby")]
public void Return_a_customer_or_throw_an_exception(string name)
{
// arrange
var expected = new Customer() { Name = name };
var customerService = new CustomerService(expected);
// act
Func<CustomerService, Customer> action = service => service.GetCustomer();
// assert
// Exception condition
if (name == null)
{
customerService.Invoking(action).Should().Throw<Exception>();
}
else
// Valid condition
{
var result = action.Invoke(customerService);
result.Should().BeEquivalentTo(expected);
}
}
}
public class Customer
{
public string Name { get; set; }
}
public class CustomerService
{
private Customer _customer;
public CustomerService(Customer customer)
{
_customer = customer;
}
public Customer GetCustomer()
{
if (_customer.Name == null) { throw new Exception(); }
return _customer;
}
}
}
References
Bonus
Here's the xunit.runner.json to show only method names in the Test Runner output. Remember to set the file to Copy to Output.
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
"methodDisplay": "method"
}