Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is using Moq pointless, since Returns() redefines the behavior of your method?

I am trying to familiarize myself with TDD. I am trying to figure out how to use Moq alongside with NUnit. I have created an ICalculator interface that has an Add(decimal num1, decimal num2) method and a Calculator class that implements it.

My problem is, by specifying what should be returned when specific values are in, am I essentially not redefining the way my method works? I mean, if I were to suddenly make the Add method multiply the numbers instead, my test would still not break as I have changed the way it works completely.

Am I missing something obvious about the purpose of Moq and how it should be used?

 var mockCalculator = new Mock<ICalculator>();
 mockCalculator.Setup(mock => mock.Add(10m, 25m)).Returns(35m);
 var calculator = mockCalculator.Object;

 var result = calculator.Add(10m, 25m);

 Assert.That(result, Is.EqualTo(35m));

I also tried following the article here, but it ends up doing the same thing - predefining the result which won't change even if the method being tested does.

like image 279
EvilBeer Avatar asked Oct 20 '25 02:10

EvilBeer


1 Answers

The point of mocking isn't to mock the central code you're testing, but to mock its dependencies. This works best when the dependencies are interfaces. Let's say you have this interface:

public interface IFileLoader {
    string loadFileContents(string fileName);
}

and a class that implements it by actually opening and reading from that file. Then, you have another class which uses that class:

public class FileAnalyzer {
    private readonly IFileLoader _fileLoader;

    public FileAnalyzer(IFileLoader fileLoader) {
        _fileLoader = fileLoader;
    }

    public string Analyze(string fileName) {
        String contents = _fileLoader.loadFileContents(fileName);
        // (Some fancy algorithm here, resulting in some return value)
    }
}

Now, you want to test FileAnalyzer.Analyze(), which presumably contains some advanced logic. Actually having to create a file on disk and passing it to that method during the test would be cumbersome; it would be easier to simply keep the input string in a variable. With a mocking framework, you can mock the dependency FileLoader, thus being able to concentrate on testing the actual logic.

[Test]
public void TestAnalyze() {
    var mockLoader = new Mock<IFileLoader>();
    mockLoader.Setup(ml => ml.loadFileContents("foo.txt")).Returns("Whatever you would have had in the file");
    var analyzer = new FileAnalyzer(mockLoader.Object);
    Assert.That(analyzer.Analyze("foo.txt"), Is.EqualTo("Expected analysis result"));
}

This test doesn't touch the file system at all, and is therefore simpler and faster. In the production (non-test) code, you would use a real implementation of IFileLoader that actually reads the file from disk: new FileAnalyzer(new RealFileLoader()). Also, notice that FileAnalyzer doesn't need to know about files or the fact that its data will come from a file when running in production and from a string when running in the test.

like image 149
Aasmund Eldhuset Avatar answered Oct 22 '25 17:10

Aasmund Eldhuset