5

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.

forsvarir
  • 10,749
  • 6
  • 46
  • 77
Pallab Pain
  • 134
  • 1
  • 11

3 Answers3

6

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));
}
Andrey Ershov
  • 1,773
  • 1
  • 16
  • 26
5

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.

forsvarir
  • 10,749
  • 6
  • 46
  • 77
-1

In your test class ,

using Math.Library;
using System;
using Moq;
using NUnit.Framework;

namespace UnitTests {
[TestFixture]
public class CMathTests {

    CMath mathObj;
    private IMath _math;
    [SetUp]
    public void Setup() {
        mathObj = new CMath();// no need for this in mocking and its a wrong      approach
        _math = new Mock<IMath>();//initialize a mock object
    }

    [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() {
    int count = 0;
    _math.setup(x =>x.Multiply(It.IsAny<Int>(),It.IsAny<Int>())).Callback(() => count++);
    _math.verify(x =>x.Multiply(),"Multiply is called"+ count+" number of times");
    }
}
}

This will work properly for your situation. Similarly you have to modify your every function as in mocking you cannot instantiate the object of your class if it implements an interface see i have made the mock object for interface.

To know more about Moq, visit here

Mohit
  • 522
  • 4
  • 10
  • also you can go for Times class in Moq. It's all upto you how you wanna go. I have given you the clear insight to use Moq concept. You can also go for the other answer posted here also. Thanks. – Mohit Apr 10 '16 at 09:42
  • this is wrong - in mock of IMath you don't have implementation of CMath, so you don't have the code to call Multiply. – Andrey Ershov Apr 10 '16 at 09:43
  • yess you are right @aershov that's what i asked pallab pain . Also i have given him the generic approach to count the call to method . – Mohit Apr 10 '16 at 09:45
  • Thank you @Mohit, but I don't see any call made to the Factorial() method in your solution. How does it satisfy the test method condition that `Multiply is called 4 times when Factorial of 4 is performed`? – Pallab Pain Apr 10 '16 at 09:47
  • sorry @pallab pain i percepted your question a lil bit wrong i thought you want to count the call to multiply function . the other solution is good for your situation. – Mohit Apr 10 '16 at 13:02