Unit Testing Interface on Implementation

c#, interface, logging, testing

Solution

You can try using the factory pattern.

What happens here is in your production code, you are getting the logger from a Factory. In this factory, it returns either your real logger, or a fake logger which is setup in your unit tests. To your production code, it makes no difference what-so-ever.

In your Unit Tests, you are using a fake logger created using Moq. This fake allows you to test that an interface method was called, in this case `ILogger.Log()`. This is done by using the `.Verify` method.

Try something like this:

ILogger.cs

public interface ILogger
{
    void Log(string message);
}

LoggerFactory.cs

public static class LoggerFactory
{
    public static ILogger Logger
    {
        get
        {
            return LoggerFactory._logger == null ? new Logger() : LoggerFactory._logger;
        }
        set
        {
            LoggerFactory._logger = value;
        }
    }
    private static ILogger _logger = null;
}

DataProvider.cs

public void GetData()
{
    var logger = LoggerFactory.Logger;

    logger.Log("..."); // etc...
}

UnitTest.cs

private void Mock<ILogger> _mockLogger = null;

public void Load()
{
    this._mockLogger = new Mock<ILogger>();

    LoggerFactory.Logger = _mockLogger.Object;
}

public void UnitTest()
{
    // test as required

    this._mockLogger.Verify(m => m.Log(It.IsAny<string>()));
}

Problem

I have an interface which is used in an MVC controller which gets some data. To keep it simple the interface so far looks something like this: ``` public interface IDataProvider { DataModel GetData(); } ``` I have appropriate unit tests for this interface where it is called in the action. However, in the real implementation this will call a web service which of course could throw an exception, therefore if it does I want to write a test to ensure that I log a message if an error occurs. To do this I have a logger interface which is actually an interface to NLog called ILogger. I could do this: ``` public interface IDataProvider { DataModel GetData(ILogger logger); } ``` This would allow me to run unit tests for the logger making it nice and simple. However, I don't think this is the right way of doing this because the logger is really unrelated to this method. Also, if I start adding other methods to this interface which I need logging for then I will have to include the logger in the parameter of all of those methods as well. The best way I can think of right now is to include the logger in the constructor of my implementation which might look like this: ``` public class DataProvider : IDataProvider { private readonly ILogger _logger; public DataProvider(ILogger logger) { _logger = logger; } public DataModel GetData() { // CODE GOES HERE } } ``` However this means that I cannot test the logger in my unit tests. What is the best way to achieve this so that I can keep the logger separate from the method and make it testable? I'll appreciate any help, thanks. EDIT: I realise I missed out unit testing code here is what I mean: At the moment I am ensuring that GetData is called in my action this way: ``` var controller = new DataController(_dataProvider.Object); controller.Index(); _dataProvider.Verify(dataProvider => dataProvider.GetData()); ``` What I'd like to do is the same but for the logger but only if an exception is thrown like this: ``` _dataProvider.Setup(dataProvider => dataProvider.GetData()).Throws<WebException>(); var controller = new DataController(_dataProvider.Object); controller.Index(); _logger.Verify(logger => logger.ErrorException(It.IsAny<string>(), It.IsAny<Exception>()); ``` Obviously logger would be given to data provider in the setup. I hope that makes a bit more sense.

Original source

Related problems