实施单元测试接口

Ser*_*uss 2 c# testing logging interface

我有一个接口,用于获取一些数据的MVC控制器.为了简单起见,到目前为止界面看起来像这样:

public interface IDataProvider
{
    DataModel GetData();
}
Run Code Online (Sandbox Code Playgroud)

我对此接口进行了适当的单元测试,在操作中调用它.但是,在实际实现中,这将调用Web服务,当然可以抛出异常,因此如果确实如此,我想编写测试以确保在发生错误时记录消息.

为此,我有一个记录器接口,它实际上是一个名为ILogger的NLog接口.我能做到这一点:

public interface IDataProvider
{
    DataModel GetData(ILogger logger);
}
Run Code Online (Sandbox Code Playgroud)

这将允许我为记录器运行单元测试,使其变得美观和简单.但是,我不认为这是正确的方法,因为记录器与此方法实际上无关.此外,如果我开始向我需要记录的接口添加其他方法,那么我将必须在所有这些方法的参数中包含记录器.

我现在能想到的最好的方法是在我的实现的构造函数中包含logger,它可能如下所示:

public class DataProvider : IDataProvider
{
    private readonly ILogger _logger;

    public DataProvider(ILogger logger)
    {
        _logger = logger;
    }

    public DataModel GetData()
    {
        // CODE GOES HERE
    }
}
Run Code Online (Sandbox Code Playgroud)

但是这意味着我无法在单元测试中测试记录器.实现这一目标的最佳方法是什么,以便我可以将记录器与方法分开并使其可测试?

我会感激任何帮助,谢谢.

编辑:

我意识到我错过了单元测试代码这里是我的意思:

目前我确保以这种方式在我的动作中调用GetData:

var controller = new DataController(_dataProvider.Object);

controller.Index();

_dataProvider.Verify(dataProvider => dataProvider.GetData());
Run Code Online (Sandbox Code Playgroud)

我想做的是相同但对于记录器,但只有在抛出异常时才会这样:

_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>());
Run Code Online (Sandbox Code Playgroud)

显然,logger将在设置中提供给数据提供者.我希望这更有意义.

rhu*_*hes 6

您可以尝试使用工厂模式.

这里发生的是你的生产代码,你从工厂获得记录器.在这个工厂中,它会返回您的真实记录器,或者在您的单元测试中设置的记录器.对于您的生产代码,它没有任何区别.

在单元测试中,您使用的是使用Moq创建的假记录器.在这种情况下,这个假允许您测试调用接口方法ILogger.Log().这是通过使用该.Verify方法完成的.

尝试这样的事情:

ILogger.cs

public interface ILogger
{
    void Log(string message);
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

DataProvider.cs

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

    logger.Log("..."); // etc...
}
Run Code Online (Sandbox Code Playgroud)

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>()));
}
Run Code Online (Sandbox Code Playgroud)