FluentArrangement is a DI container designed for use in the 'arrange' phase of a unit test.
It provides a fluent interface to configure auto mocking and random test data generation.
private IFixture Fixture = new Fixture()
.UseDefaults()
.UseConstructorsAndProperties()
.UseProxyObjects()
.ForType<MyOtherType>(f => f
.UseRandomValues())
.Use(new MyCustomFactory());
Use it to generate your SUT (System Under Test).
[Fact]
public Task GetReturns404()
{
var controller = Fixture.Create<MyController>();
var result = await controller.GetAsync("id");
Assert.Equals(404, result.StatusCode);
}
Further customize the fixture in a test method.
Factory priority is in reverse order, so if many factories can create the same type, the last one registered will be used.
[Fact]
public Task GetReturns200WhenFlagIsTrue()
{
Fixture.ForProperty((MySettings s) => s.MyFlag,
f => f.UseValue(true));
var controller = Fixture.Create<MyController>();
var result = await controller.GetAsync("id");
Assert.Equals(200, result.StatusCode);
}
All requests are monitored, allowing you to verify calls to dependencies.
Combine with FluentAssertions for maximum fluentness.
[Fact]
public Task GetCallsRepositoryOnce()
{
var controller = Fixture.Create<MyController>();
var result = await controller.GetAsync("id");
Fixture.Requests.GetMethodCalls()
.ToType<IMyRepository>()
.Should().ContainSingle()
.Which.Method.Name.Should().Be("Add");
}
This is largely influenced by AutoFixture and AutoFixture.AutoMoq, which I've used daily for several years. I love it, but I've found some concepts difficult to learn and to teach, so I'd like to try simplify those ideas.
- Everything is a factory. All creation algorithms are explicitly registered. Add some of the built-in randomisation, or use your own, or configure a combination.
- No need for Moq. The type interception factory just returns from the fixture. It even works with generics.
- Monitoring is built-in. No need to Freeze Mocks and Verify. Just act, then assert against the traced requests to the factories.
- Scopable. Apply factories to types nested inside others. Useful when your dependency's dependencies depend on certain settings.
- Natural configuration. The fluent interface means less documentation and more intellisense.