Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to verfiy that a method has been called a certain number of times using Moq?

I have the following implementation,

public interface IMath {
    double Add(double a, double b);
    double Subtract(double a, double b);
    double Divide(double a, double b);
    double Multiply(double a, double b);
    double Factorial(int a);
}

public class CMath: IMath {
    public double Add(double a, double b) {
        return a + b;
    }

    public double Subtract(double a, double b) {
        return a - b;
    }

    public double Multiply(double a, double b) {
        return a * b;
    }

    public double Divide(double a, double b) {
        if (b == 0)
            throw new DivideByZeroException();
        return a / b;
    }

    public double Factorial(int a) {
        double factorial = 1.0;
        for (int i = 1; i <= a; i++)
            factorial = Multiply(factorial, i);
        return factorial;
    }
}

How can I test that Multiply() is called n times when n's Factorial is calculated?

I'm using NUnit 3 and Moq. Following are the tests that I have already written,

[TestFixture]
public class CMathTests {

    CMath mathObj;

    [SetUp]
    public void Setup() {
        mathObj = new CMath();
    }

    [Test]
    public void Add_Numbers9and5_Expected14() {
        Assert.AreEqual(14, mathObj.Add(9, 5));
    }

    [Test]
    public void Subtract_5From9_Expected4() {
        Assert.AreEqual(4, mathObj.Subtract(9, 5));
    }

    [Test]
    public void Multiply_5by9_Expected45() {
        Assert.AreEqual(45, mathObj.Multiply(5, 9));
    }

    [Test]
    public void When80isDividedby16_ResultIs5() {
        Assert.AreEqual(5, mathObj.Divide(80, 16));
    }

    [Test]
    public void When5isDividedBy0_ExceptionIsThrown() {
        Assert.That(() => mathObj.Divide(1, 0),
            Throws.Exception.TypeOf<DivideByZeroException>());
    }

    [Test]
    public void Factorial_Of4_ShouldReturn24() {
        Assert.That(mathObj.Factorial(4), Is.EqualTo(24));
    }

    [Test]
    public void Factorial_Of4_CallsMultiply4Times() {

    }
}

I'm fairly new to using Moq so I'm not quite getting it at the moment.

like image 345
Pallab Pain Avatar asked Apr 10 '16 09:04

Pallab Pain


People also ask

Which method of expect () is used to count the number of times a method to have been called?

Using Verify This is probably the best way to go as Verify is designed for this exact purpose - to verify the number of times a method has been called.

What is times once in MOQ?

Between - Specifies that a mocked method should be invoked between from and to times. Exactly - Specifies that a mocked method should be invoked exactly times times. Never - Specifies that a mocked method should not be invoked. Once - Specifies that a mocked method should be invoked exactly one time.

What is CallBase in MOQ?

CallBase , when initialized during a mock construction, is used to specify whether the base class virtual implementation will be invoked for mocked dependencies if no setup is matched. The default value is false . This is useful when mocking HTML/web controls of the System.


2 Answers

You need to separate mocked part and tested part, cause Moq is about eliminating dependencies, and your CMath class does not have them!

But basically you don't need to test, that Multiply is called 4 times - it's internal implementation. Test results :)


approach 1 - split classes

create Separate Factorial class, so that multiply will be in separate interface.

 public interface IMath {
        double Add(double a, double b);
        double Subtract(double a, double b);
        double Divide(double a, double b);
        double Multiply(double a, double b);      
    }
 public interface IFactorial {
       double Factorial(int a, IMath math);
}

And in your test you can create Mock of IMath

[Test]
public void Factorial_Of4_CallsMultiply4Times()
{
    var mathMock = new Mock<IMath>();
    var factorial = new Factorial();
    factorial.Factorial(4, mathMock.Object);
    mathMock.Verify(x => x.Multiply(It.IsAny<double>()), Times.Exactly(4));
}

approach 2 - optional injected delegate

public double Factorial(int a, Func<double,double,double> multiply = null)
{
    multiply = multiply ?? CMath.Multiply;
    double factorial = 1.0;
    for (int i = 1; i <= a; i++)
        factorial = multiply(factorial, i);
    return factorial;
}


[Test]
public void Factorial_Of4_CallsMultiply4Times()
{
    var mathMock = new Mock<IMath>();
    var math = new CMath();
    math.Factorial(4, mathMock.Object.Multiply);
    mathMock.Verify(x => x.Multiply(It.IsAny<double>()), Times.Exactly(4));
}
like image 192
Andrey Ershov Avatar answered Oct 21 '22 10:10

Andrey Ershov


As has already been said by @aershov, you don't need to test if Multiply has been called 4 times. It's an implementation detail that you're already testing to an extent in your test Factorial_Of4_ShouldReturn24. You may want to consider using the TestCase attribute to allow you to supply a range of inputs to your tests, rather than a single value:

[TestCase(4, 24)]
[TestCase(2, 2)]
[TestCase(1, 1)]
[TestCase(0, 1)]
public void Factorial_OfInput_ShouldReturnExpected(int input, int expectedResult)
{
    Assert.That(mathObj.Factorial(input), Is.EqualTo(expectedResult));
}

@aershov covered two design changes that would allow you to mock the interaction you are asking about. A third and arguably the least impactful change would be to make your Multiply method virtual. This would allow you to use a partial mock to validate the interaction. The changes would look like this:

Implementation

public class CMath : IMath
{
    public virtual double Multiply(double a, double b)
    {
        return a * b;
    }
    // ...

Test

Mock<CMath> mockedObj;
CMath mathObj;

[SetUp]
public void Setup()
{
    mockedObj = new Mock<CMath>();
    mockedObj.CallBase = true;
    mathObj = mockedObj.Object;
}

[Test]
public void Factorial_Of4_CallsMultiply4Times()
{
    mathObj.Factorial(4);
    mockedObj.Verify(x => x.Multiply(It.IsAny<double>(), 
                                     It.IsAny<double>()), Times.Exactly(4));
}

I'm not a huge fan of mocking the system under test (it's generally a good sign that you're doing something wrong), however it does allow you to do what you're asking.

Mocks can be very useful, however when you're using them you need to think carefully about what it is you're actually trying to test. Looking at the test above, it could be satisfied with the code below:

public double Factorial(int a)
{
    double factorial = 1.0;
    for (int i = 1; i <= a; i++)
        factorial = Multiply(factorial, a);
    return factorial;
}

This code has a critical bug in it, it passes the source argument in to each iteration of the loop, rather than the loop counter. The result is very different, but the number of calls is the same, so the test still passes. This is a good indication that the test isn't actually adding value.

In fact, the test actually causes increased friction in that it's harder to change the implementation of the Factorial function. Consider an example of 4!. The calculations required are 4*3*2*1, however the last step the multiplication by 1 is essentially a NOP, since n*1=n. With this in mind it would be possible to slightly optimize your factorial method to:

public double Factorial(int a)
{
    double factorial = 1.0;
    for (int i = 2; i <= a; i++)
        factorial = Multiply(factorial, i);
    return factorial;
}

The input/output tests on the factorial method will continue to work, however the Mocked test that counts the number of calls to Multiply breaks because only 3 calls are necessary in order to calculate the answer.

Always consider the benefits and costs when deciding to use mock objects.

like image 27
forsvarir Avatar answered Oct 21 '22 10:10

forsvarir