Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test if method in ClassA has been called from another method in ClassA

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?

like image 428
Hoppe Avatar asked Jun 30 '15 14:06

Hoppe


2 Answers

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.

like image 150
Andrew Whitaker Avatar answered Oct 13 '22 00:10

Andrew Whitaker


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.

like image 42
forsvarir Avatar answered Oct 13 '22 00:10

forsvarir