FluentArrange lets you write clean Arrange blocks in your unit tests even when the constructor of the class under test has a lot of (mocked) dependencies.
| Package | Version | Description |
|---|---|---|
FluentArrange |
Core package | |
FluentArrange.NSubstitute |
When you use NSubstitute for mocking |
Consider the following example where we use NSubstitute to mock dependencies:
var sut = new ResetPasswordController(
Substitute.For<IAccountService>(),
Substitute.For<IAuditService>(),
Substitute.For<IMailService>());With FluentArrange, an instance T is instantiated using Arrange.Sut<T>, and all of its constructor dependencies are auto-mocked:
var sut = Arrange.Sut<ResetPasswordController>();Adding new dependencies will not break existing unit tests.
Most of the time, we need to arrange some behavior for our mocked dependencies:
var accountService = Substitute.For<IAccountService>();
accountService.FindEmail("[email protected]").Returns(new Account("foo"));
var mailService = Substitute.For<IMailService>();
mailService.SendMail("[email protected]").Returns(true);
var sut = new ResetPasswordController(
accountService,
Substitute.For<IAuditService>(),
mailService);With FluentArrange, you can use the Fluent API WithDependency<T> to achieve the exact same result as the code above:
var sut = Arrange.For<ResetPasswordController>()
.WithDependency<IAccountService>(x => x.FindEmail("[email protected]").Returns(new Account("foo")))
.WithDependency<IMailService>(x => x.SendMail("[email protected]").Returns(true));As you might have noticed, we did not need to write arrange code for IAuditService, as that will be automatically created for you.
The only time you need to call WithDependency<T> is when you need to arrange the behavior of type T.
Sometimes, you may want to use a fake implementation rather than letting FluentArrange automatically create mocked instances.
In that case, you can provide an instance using WithDependency<T>(T):
var context = Arrange.For<ResetPasswordController>()
.WithDependency<IAccountService>(new InMemoryAccountService());or WithDependency<T>(T, Action<T>) if you need to do more arranging:
var context = Arrange.For<ResetPasswordController>()
.WithDependency<IAccountService>(new InMemoryAccountService(), d =>
{
d.AddAccount("foo", "[email protected]");
d.AddAccount("foobar", "[email protected]");
});or WithDependency<T, T2>(T2, Action<T2>) if you need to call T2-specific methods:
var context = Arrange.For<ResetPasswordController>()
.WithDependency<IAccountService, InMemoryAccountService>(new InMemoryAccountService(), d =>
{
d.AddTestAccounts();
});Suppose you need to assert that a dependency's method has been called.
Well, you simply arrange code with Arrange.For<T> instead of Arrange.Sut<T> to get a FluentArrangeContext object:
var context = Arrange.For<ResetPasswordController>();To get the SUT, call the Sut property of the context:
var sut = context.Sut;To get the Dependency, simply call Dependency<T>:
context.Dependency<IAccountService>();
or Dependency<T, T2> to get access to T2 and its specific methods:
context.Dependency<IAccountService, AccountService>();
When used together, a unit test could look like this:
// Arrange
var context = Arrange.For<ResetPasswordController>();
// Act
context.Sut.Reset("[email protected]");
// Assert
context.Dependency<IAccountService>().Received(1).FindEmail("[email protected]");