Skip to content

Unit test for a method that accepts builder-style parameter with NSubstitute

Introduction

Sometimes it happens that you should write unit tests…

Just kidding – you HAVE TO write unit tests (me as well).

In a perfect world the method we want to write a unit-test for is well-written, concise and returns concrete value. Unfortunately, there are methods that seems to be intricate in terms of unit tests, at least at first glance. Let’s consider the following scenario:

public class CustomClass
{
    private readonly ICustomService _customService;

    public CustomClass(ICustomService customService)
    {
        _customService = customService;
    }

    public void RunProcess(int param_1, int param_2, int param_3)
    {
        _customService.Run(
            descriptorBuilder => descriptorBuilder
                .BuildMyProperty_1(param_1)
                .BuildMyProperty_2(param_2)
                .BuildMyProperty_3(param_3)
        );
    }
}

public interface ICustomService
{
    void Run(Func<IDescriptor, IDescriptor> descriptorBuilder);
}

public interface IDescriptor
{
    public IDescriptor BuildMyProperty_1(int myProperty_1);
    public IDescriptor BuildMyProperty_2(int myProperty_2);
    public IDescriptor BuildMyProperty_3(int myProperty_3);
}

Having class like this we want to write unit test for the RunProcess method. The method accepts three int parameters. Eventually the parameters are passed to the ICustomService.Run method that accepts builder-style parameter. Moreover, the RunProcess returns nothing.

Unit test using NSubstitute

NSubstitute might be your best friend when it comes to mock objects and assert received calls. The syntax is succinct and intuitive so I would suggest you to consider to experiment with the library.

Let’s build unit test in a AAA manner – arrange, act, assert – which NSubstitute is designed for.

The test name is RunProcess_CustomServiceExecutedWithPassedParameters_Success.
The name follows the pattern: <MethodNameThatIsBeingTested>_<ExpectedInternalMethodBehaviour>_<Status>.
I guess there are as many test naming conventions as engineers that create it. Furthermore, I believe that as long as the convention informs you well about test internals it is good enough.

The test looks like this:

[Fact]
public void RunProcess_CustomServiceExecutedWithPassedParameters_Success()
{
    //arrange
    var callParameters = (111, 22, 3333);

    var mockDescriptor = Substitute.For<IDescriptor>();
    mockDescriptor.ReturnsForAll<IDescriptor>(mockDescriptor); 

    var customService = Substitute.For<ICustomService>();
    CustomClass customClass = new CustomClass(customService);

    //act
    customClass.RunProcess(
        param_1: callParameters.Item1,
        param_2: callParameters.Item2,
        param_3: callParameters.Item3
    );

    //assert
    customService
        .Received(1)
        .Run(Arg.Is<Func<IDescriptor, IDescriptor>>(
                descriptorBuilder => AssertDescriptorFunc(
                    descriptorBuilder,
                    mockDescriptor,
                    callParameters
                )
            )
        );
}

private bool AssertDescriptorFunc(
    Func<IDescriptor, IDescriptor> descriptorFunc,
    IDescriptor mockDescriptor,
    (int, int, int) callParameters
)
{
    descriptorFunc(mockDescriptor);

    mockDescriptor.Received(1).BuildMyProperty_1(callParameters.Item1);
    mockDescriptor.Received(1).BuildMyProperty_2(callParameters.Item2);
    mockDescriptor.Received(1).BuildMyProperty_3(callParameters.Item3);

    return true;
}

Let’s briefly summarize what has happened up there. The test asserts that after the customClass.RunProcess invocation the customService.Run method received exactly one call. The invocation happened with exactly one parameter. The parameter was type of Func<IDescriptor,IDescriptor> and then was asserted using AssertDescriptorFunc method.

Summary

To sum up, thanks to the NSubstitute library we are able to write unit test even for such an unusual scenario. NSubstitute offers much more than I’ve just presented here. I highly recommend to check the NSubstitute documentation and spend some time on playing with its functionalities.

If you want to give it a try you can find attached scenario in my github, project: nsubstitute_1, test project: nsubstitute_1-test.

Have a nice day, bye!

Published in.NET

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *