Макетирование и тестирование сообщения LogError с использованием Moq и xUnit - PullRequest
1 голос
/ 30 октября 2019

У меня есть уровень класса ILogger, который настроен с помощью ILoggerFactory в конструкторе. Регистратор тогда используется в методе в пределах этого класса, и это работает отлично.

Я изо всех сил пытаюсь смоделировать ILogger и ILoggerFactory, чтобы я мог выполнить модульное тестирование сообщения LogError. Может кто-нибудь показать мне пример этого?

Я использую xUnit и Microsoft.Extentions.Logging для логина

//This is my unit test project
[Fact]
public void TestLogErrorMessage()
{
MyClass myClass = new MyClass  (MockLoggerFactory().Object);

    var result = myClass.Mymethod("a")

    //How do I test the LogError message???
}


//Not sure if this is correct
private Mock<ILoggerFactory> MockLoggerFactory()
{
           Mock<ILoggerFactory> mockLoggerFactory = new 
Mock<ILoggerFactory>(MockBehavior.Default);
    mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>()))
                           .Returns(MockLogger().Object);

        return mockLoggerFactory;
}

private Mock<ILogger> MockLogger()
{        
       var logger = new Mock<ILogger>();
       return logger;
}

//This is the class/method i need to test
private readonly ILogger logger;

public MyClass(ILoggerFactory loggerFactory)
{
       if (loggerFactory != null)
       {
           this.logger = loggerFactory.CreateLogger<MyClass>();
       }
}


public string Mymethod(string msg)
{
       if(msg = "a")
       {
           this.logger.LogError($"error");
       }

       return "a string";
 }

1 Ответ

0 голосов
/ 30 октября 2019

Вы можете сделать что-то вроде этого

( примечание: это не работает в данном конкретном случае, потому что LogError - это метод расширения, обновление см. Ниже ):

Mock<ILogger> _logger; // declare it somewhere where your test can access it

[Fact]
public void TestLogErrorMessage()
{
    MyClass myClass = new MyClass(MockLoggerFactory().Object);

    var result = myClass.Mymethod("a")

    _logger.Verify();
}


private Mock<ILoggerFactory> MockLoggerFactory()
{
    _logger = new Mock<ILogger>();
    _logger.Setup(log => log.LogError(It.IsAny<string>())).Verifiable();
    Mock<ILoggerFactory> mockLoggerFactory = new Mock<ILoggerFactory>(MockBehavior.Default);
    mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>()))
                           .Returns(_logger.Object);

    return mockLoggerFactory;
} 

Ключевым моментом, на который следует обратить внимание, является вызов Verifiable() после настройки макета ILogger. Вы можете прочитать немного больше о Verifiable() в этом ТАКОМ вопросе . После выполнения теста вы можете проверить, был ли вызван метод, вызвав .Verify() на макете.

В зависимости от ваших потребностей, вы можете установить это в конструкторе вашего тестового класса (если он вам нужен)для всех / большинства тестов) или непосредственно внутри вашего метода тестирования. Вы также можете вернуть его вместе с ILoggerFactory. Смысл в том, чтобы удерживать регистратор, чтобы вы могли проверить по нему, что метод был вызван.

В вашем конкретном случае (при попытке проверить вызовы ILogger.LogError) вы не должны издеваться над LogError, нобазовый метод ILogger.Log. Это может выглядеть так:

var formatter = new Func<It.IsAnyType, Exception, string>((a, b) => "");
m.Setup(x => x.Log<It.IsAnyType>(LogLevel.Error, 
                                 It.IsAny<EventId>(),
                                 It.IsAny<It.IsAnyType>(),
                                 It.IsAny<Exception>(), 
                                 formatter))
  .Verifiable();

Другая альтернатива - создать поддельную реализацию ILogger и вернуть ее из Mock<ILoggerFactory>:

mockLoggerFactory.Setup(_ => _.CreateLogger(It.IsAny<string>())
                 .Returns(new FakeLogger());

public class FakeLogger : ILogger
{
    public static bool LogErrorCalled { get; set; }

    public IDisposable BeginScope<TState>(TState state)
    {
        throw new NotImplementedException();
    }

    public bool IsEnabled(LogLevel logLevel) => true;

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        if(logLevel == LogLevel.Error)
        {
            LogErrorCalled = true;
        }
    }
}

А затем после вашеготест вы можете проверить, является ли FakeLogger.LogErrorCalled true. Это упрощено для вашего конкретного теста - вы можете получить более сложный, чем это, конечно.

...