FluentArrange using NSubstitute as mocking framework.


Keywords
AAA, TDD, mocking, unit-testing, nsubstitute, csharp, dotnet, dotnet-standard, fluent, unit-test, sut, auto-mocking, arrange-act-assert, sut-factory
License
Apache-2.0
Install
Install-Package FluentArrange.NSubstitute -Version 0.10.0

Documentation

dotnet-dash

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.

Packages

Package Version Description
FluentArrange Nuget version Core package
FluentArrange.NSubstitute Nuget version When you use NSubstitute for mocking

Auto-Mocking Ctor Dependencies

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.

Arranging Dependencies Fluently

Most of the time, we need to arrange some behavior for our mocked dependencies:

var accountService = Substitute.For<IAccountService>();
accountService.FindEmail("foo@foo.com").Returns(new Account("foo"));

var mailService = Substitute.For<IMailService>();
mailService.SendMail("foo@foo.com").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("foo@foo.com").Returns(new Account("foo")))
    .WithDependency<IMailService>(x => x.SendMail("foo@foo.com").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.

Passing an instance instead

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", "foo@foo.com");
        d.AddAccount("foobar", "foobar@foo.com");
    });

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();
    });

Asserting Dependencies

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("foo@foo.com");

// Assert
context.Dependency<IAccountService>().Received(1).FindEmail("foo@foo.com");