Мокинг и тестирование сообщения LogError с помощью Moq и xUnit
У меня есть 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";
}
2 ответа
Это то, что наконец сработало для меня, успешная проверка вызова LogError:
var loggerMock = new Mock<ILogger>();
Expression<Action<ILogger>> logAction = x => x.Log<It.IsAnyType>(LogLevel.Error,//< you can change this up with other severity levels
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>());
loggerMock.Setup(logAction).Verifiable();
//call a method that calls LogError here.
loggerMock.Object.LogError("test");
loggerMock.Verify(logAction, Times.Once);
Вы могли бы сделать что-то вроде этого
(примечание: это не работает в данном конкретном случае, потому что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
. Это упрощено для вашего конкретного теста - вы, конечно, можете сделать более подробный.