Using the parameters of a method in a Moq setup to define the return value


Using the parameters of a method in a Moq setup to define the return value

Usually, when creating unit tests it’s best to keep it as simple as possible, so whenever the tests fail, it’s simple to identify the reason. However that’s not always possible. Sometimes our code is not fully testable, resulting in complex setups, for example. That can cause us to use more complicated mockings to achieve our objective.

TL;DR – Using Moq setup to return a property of the parameter

If you are only looking for a sample code, look no further:

mock.Setup(x => x.Method(It.IsAny<IInterface>())).Returns<IInterface>(x=> x.Property);

Detailed explanation

Scenario

Let’s suppose we are creating an application to list and sell plots. The flow starts by adding plots to the PlotSellingPage class. Whenever we add a plot to the PlotSellingPage, we call the PlotPriceCalculator to calculate its price. Let’s take a look at our code files.

Plot

public interface IPlot
{
    double SquareMeters { get; }
}

public class SquarePlot : IPlot
{
    public  double SquareMeters { get; }

    public SquarePlot(double size)
    {
        SquareMeters = size * size;
    }
}

PlotPriceCalculator

public interface IPlotPriceCalculator
{
    double GetPrice(IPlot plot);
}

public class PlotPriceCalculator : IPlotPriceCalculator
{
    public double GetPrice(IPlot plot)
    {
        // Let's pretend some really crazy logic goes on in here
    }
}

PlotSellingPage

public class PlotSellingPage
{
    private readonly IPlotPriceCalculator _plotPriceCalculator;

    private Dictionary<IPlot, double> _plotsForSale = new();

    public PlotSellingPage(IPlotPriceCalculator plotPriceCalculator)
    {
        _plotPriceCalculator = plotPriceCalculator;
    }

    public void RegisterPlotForSale(IPlot plot)
    {
        var price = _plotPriceCalculator.GetPrice(plot);

        _plotsForSale.Add(plot, price);
    }

    public double GetValueSum()
    {
        return _plotsForSale.Sum(x => x.Value);
    }
}

Testing the GetValueSum method

Our task here is to verify that the GetValueSum does its job correctly. In order to do it, we are creating several plots and adding them to the PlotSellingPage, while also adding to the expectedSum variable to assert later that the actual value is correct.

[Test]
public void ShouldSumCorrectly()
{
    // Arrange
    var calculatorMock = new Mock<IPlotPriceCalculator>();
    calculatorMock.Setup(x => x.GetPrice(It.IsAny<IPlot>())).Returns<IPlot>(plot => plot.SquareMeters);

    var page = new PlotSellingPage(calculatorMock.Object);

    double expectedSum = 0;
    var random = new Random();
    for (int i = 0; i < 10; i++)
    {
        var size = random.Next(0, 10);
        var plot = new SquarePlot(size);
        expectedSum += plot.SquareMeters;
        page.RegisterPlotForSale(plot);
    }

    // Act
    var actualSum = page.GetValueSum();

    // Assert
    Assert.That(actualSum, Is.EqualTo(expectedSum));
}

The trick lies in the setup on line 6: every time we call the GetPrice method in the PlotPriceCalculator mock, we are returning the SquareMeters property of the IPlot parameter provided to it. That is a very simple but logical setup for it, that allows us to prepare our class without exposing any unnecessary fields or properties. Be careful to not get tricked by the “<IPlot>” in “Returns<IPlot>”, as we might expect it to refer to the returning type when it actually refers to the input parameter type.

Another possible solution for this situation would be to use a Fake IPlotPriceCalcuator. Fakes are classes that have simpler and working implementations, allowing us to avoid working around the complex logic of production ones. You can read more about fakes, mocks and stubs here.