Как протестировать встроенный ядро ​​asp.net

Я хочу проверить, что некоторые журналы зарегистрированы. Я использую встроенный ядро ​​asp.net, встроенный в ILogger, и вставляю его с встроенным DI-сервером asp.net:

private readonly ILogger<InvoiceApi> _logger;

public InvoiceApi(ILogger<InvoiceApi> logger)
{
    _logger = logger;
}

то я использую его как: _logger.LogError("error));

Я попытался издеваться над ним (с moq), как обычно:

MockLogger = new Mock<ILogger<InvoiceApi>>();

и введите это в службу для тестирования:

new InvoiceApi(MockLogger.Object);

затем попробовал проверить:

MockLogger.Verify(m => m.LogError(It.Is<string>(s => s.Contains("CreateInvoiceFailed"))));

но он бросает:

Неверная проверка для не виртуального (переопределяемого в VB) члена: m = > m.LogError

Итак, как я могу проверить эти журналы?

Ответ 1

Как уже сказал @Nkosi, нельзя издеваться над методом расширения. Что вы должны высмеивать, так это метод ILogger.Log, который LogError вызывает в. Это делает код проверки немного неуклюжим, но он должен работать:

MockLogger.Verify(
    m => m.Log(
        LogLevel.Error,
        It.IsAny<EventId>(),
        It.Is<FormattedLogValues>(v => v.ToString().Contains("CreateInvoiceFailed")),
        It.IsAny<Exception>(),
        It.IsAny<Func<object, Exception, string>>()
    )
);

(не уверен, что это скомпилируется, но вы понимаете суть)

Ответ 2

Я написал короткую статью, в которой показаны различные подходы, в том числе насмешка над базовым методом Log(), как описано в других ответах здесь. Статья включает в себя полное репозиторий GitHub с каждым из различных вариантов. В конце я рекомендую использовать собственный адаптер, а не работать напрямую с типом ILogger, если вам нужно проверить, вызывается ли он.

https://ardalis.com/testing-logging-in-aspnet-core

Ответ 3

LogError - это метод расширения (статический), а не метод экземпляра. Вы не можете "напрямую" имитировать статические методы (следовательно, метод расширения) с издевательской структурой, поэтому Moq не может издеваться и, следовательно, проверять этот метод. Я видел предложения в Интернете об адаптации/упаковке целевого интерфейса и выполнении ваших макетов, но это означало бы перезаписывать, если вы использовали стандартный ILogger по всему вашему коду во многих местах. Вам нужно было бы создать 2 новых типа, один для класса-оболочки, а другой для макетируемого интерфейса.