6

It is possible to test if a method has been called using Moq and dependency injection. However, is it possible to test if one method in a class calls another within the same class?

For example, I want to test that if I log a certain exception, that an information message is logged as well.

The method is:

public void Error(string message, Exception exception, long logId = 0)
{
    var int32 = (int)logId;
    Info("Id was converted to an int so that it would fit in the log: " + logId, int32);
    Error(message, exception, int32);
}

This was my attempt at unit testing it. The test fails, is there any way that it can it be done?

void logging_an_error_with_a_long_id_also_logs_info()
{
    var mock = new Mock<ILogger>();
    var testedClass = new Logger();
    var counter = 0;

    testedClass.Error("test" + counter++, new Exception("test" + counter), Int64.MaxValue);

    mock.Verify(m => m.Info(It.IsAny<string>(), It.IsAny<int>()));
}

Since the Info and Error methods are in the same class (ClassA), I don't believe I can pass ClassA as a dependency into ClassA. So does it not need tested?

Community
  • 1
  • 1
Hoppe
  • 6,508
  • 17
  • 60
  • 114
  • Have a look [here](http://stackoverflow.com/questions/1417048/how-to-verify-another-method-in-the-class-was-called-using-moq). – Daniel Dušek Jul 02 '15 at 10:42

2 Answers2

7

The best you're going to be able to do is to make Info virtual. This will allow you to create a Mock<Logger>, set CallBase = true, and verify that Info was called.

var mock = new Mock<Logger> 
{
    CallBase = true
};

mock.Object.Error("test" + counter++, new Exception("test" + counter), Int64.MaxValue);

mock.Verify(m => m.Info(It.IsAny<string>(), It.IsAny<int>()));

This way, you're still calling the actual implementation of Error, but you've used Moq to verify the Info method was called.

Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
5

It feels like you're trying to test the wrong thing. It's not really important that the Info method on your class is called from the Error method, what's important is that the behaviour of the Info method occurs. How it happens is an implementation detail of the class.

If I had a math class with two functions:

public int Mult(int x, int y) {
    return x*y;
}

public int Sqr(int x) {
    return Mult(x,y);
}

I wouldn't test that calling Sqr called out to the Mult function, I would test Sqr(4)==16. It doesn't matter if that calculation takes place in the Sqr method, or in another method of the class.

Whilst @Andrew's solution is probably what you're after, mocking the class you're testing tends to lead to tightly coupled, brittle tests.

If it's impractical to test the call by observing it's side effects, then it may be a sign that the implementation could use a bit of refactoring.

Community
  • 1
  • 1
forsvarir
  • 10,749
  • 6
  • 46
  • 77
  • It's easier to say there's probably a better way than to provide a better way – Hoppe Jul 02 '15 at 11:57
  • @Hoppe Whilst that's true, in order to provide a better way one often needs more context than is provided (for example I have no idea what the contents of your Error or Info methods is). Sometimes the best that can be done is to indicate an alternate direction, which in some will open a door that will allow them to look at their problem in a different way.. – forsvarir Jul 02 '15 at 12:10