简体   繁体   中英

What is purpose of mocking a class like Calculator?

I've been using TDD for some time but now I'm looking at mocking frameworks and I don't get some things. This question might sound stupid for someone experienced but I just don't get it. The library I use is Moq + xUnit.

Question

What's the point of testing Calculator class if I explicitly say that 2 + 2 will return 4 on this line mock.Setup(x => x.Add(2, 2)).Returns(4); and then assert it?

Of course result will be 4, I just "forced" it to return 4 in few lines above the test itself. And now even in my implementation if I do return a * b; instead of return a + b; the test will pass.

Here is another example of this same calculator tests. http://nsubstitute.github.io/

Example Code

namespace UnitTestProject1
{
    using Xunit;
    using Moq;

    public class CalculatorTests
    {
        private readonly ICalculator _calculator;

        public CalculatorTests()
        {
            var mock = new Mock<ICalculator>();

            mock.Setup(x => x.Add(2, 2)).Returns(4);
            mock.Setup(x => x.Subtract(5, 2)).Returns(3);

            this._calculator = mock.Object;
        }

        [Fact]
        public void Calculator_Should_Add()
        {
            var result = _calculator.Add(2, 2);

            Assert.Equal(4, result);
        }

        [Fact]
        public void Calculator_Should_Subtract()
        {
            var result = _calculator.Subtract(5, 2);

            Assert.Equal(3, result);
        }
    }

    public class Calculator : ICalculator
    {
        public int Add(int a, int b)
        {
            return a + b;
        }

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

    public interface ICalculator
    {
        int Add(int a, int b);
        int Subtract(int a, int b);
    }
}

The purpose is to be able to test classes depending on calculator without having the calculator is self. In your test you know that the calculator can't be the cause of failure, because is returning the correct answer.

By isolating the code under test you will be able to test real units of code. And also see exactly what is causing your test failure.

You should not unit test the mock. Suppose you want to test an object OrderProcessor which uses IService and IStorage.

To "Unit Test" OrderProcessor you simulate the behavior of IService and IStorage so that you can verify that your target class works as expected without using a Web Service and a Database.

Ie

class OrderProcessor{
 private IService service, IStorage storage;
 public OrderProcessor(IService service, IStorage storage){ // bla bla}

 public ProcessOrder(Order o){
   // do something

  // use the service
  var price = service.GetPrice(..);


  // store the result
  storage.StoreOrder(order);
 }
}

// test. Define mocks
var mockStorage = new Mock<IStorage>();
var mockService = new Mock<IService>();

// Setup test behaviour
mockStorage.Setup(m => m.GetPrice("X10").Returns(11);
mockStorage.Setup(m => m.GetPrice("X11").Returns(99);
...
var target = new OrderProcessor(mockService.Object, mockStorage.Object);

// ...
target.ProcessOrder(o);

// Verify the storing was called
mockStorage.Verify(m => m.StoreOrder(o), Times.Once());

// Verify the service was called X times
mockService .Verify(m => m.GetPrice(x), Times.Exactly(order.Items.Count));

In this case, there's no point in mocking -- the example is too simple. You're not gaining anything by mocking ICalculator .

You mock when you have a complex implementation, and you're trying to test something that depends on an implementation of that interface. You're not doing that in this case, you're testing a mock implementation. Testing a mock implementation is pointless.

For example, let's say your calculator implementation actually made a call to a web service to perform the calculations, and you were trying to test something that consumed calculations from the service. Your goal isn't to test the calculator -- your goal is to test the thing that uses the calculator. Having your test depend on a web service being up and running is silly, and could lead to your test failing unexpectedly.

Mocks are used in place of dependencies.

For example:

public interface IAddModule
{
    int Add(int lhs, int rhs);
}

public class Calculator
{
    private readonly IAddModule _addModule;

    public Calculator(IAddModule addModule)
    {
        _addModule = addModule;
    }

    public int Add(int lhs, int rhs)
    {
        return _addModule.Add(lhs, rhs);
    }
}

The Calculator class depends on the IAddModule . Depending on how IAddModule is implemented it might have side effects like logging or unmanaged code. To isolate the dependency you use a Mock in place of the IAddModule to test the class.

public class CalculatorTests
{
    private readonly Calculcator _calculator;

    public CalculatorTests()
    {
        var mock = new Mock<IAddModule>();
        mock.Setup(a => a.Add(2, 2)).Returns(4);
        _calculator = new Calculator(mock.Object);
    }

    [Fact]
    public void Given_2_And_2_Then_4_Is_Returned()
    {
        var result = _calculator.Add(2, 2);

        Assert.Equal(4, result);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM