このサンプルコードをテストするためにxUnitプロジェクトを作成しました
public class ClassToTest
{
private readonly ILogger<ClassToTest> _logger;
public ClassToTest(ILogger<ClassToTest> logger)
{
_logger = logger;
}
public void Foo() => _logger.LogError(string.Empty);
}
ロガーのモックインスタンスを作成するためにMoqをインストールしました
public class ClassToTestTests
{
private readonly ClassToTest _classToTest;
private readonly Mock<ILogger<ClassToTest>> _loggerMock;
public ClassToTestTests()
{
_loggerMock = new Mock<ILogger<ClassToTest>>();
_classToTest = new ClassToTest(_loggerMock.Object);
}
[Fact]
public void TestFoo()
{
_classToTest.Foo();
_loggerMock.Verify(logger => logger.LogError(It.IsAny<string>()), Times.Once);
}
}
テストを実行すると、このエラーメッセージが表示されます
System.NotSupportedException:サポートされていない式:logger => logger.LogError(It.IsAny()、new [] {})
System.NotSupportedExceptionサポートされていない式:logger => logger.LogError(It.IsAny()、new [] {})拡張メソッド(ここでは:LoggerExtensions.LogError)は、セットアップ/検証式では使用できません。
いくつかの調査の結果、すべてのログメソッドは単なる拡張メソッドであることがわかりました。Moqは拡張メソッドを設定できません。
この問題のために追加のサードパーティパッケージをインストールすることは避けたいと思います。テストに合格するための解決策はありますか?
拡張メソッドをモックすることはできません。
嘲笑する代わりに
logger.LogError(...)
インターフェイスメソッドをモックする必要があります
void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);
LogErrorは、実際にはこのようなインターフェイスメソッドを呼び出します
logger.Log(LogLevel.Error, 0, new FormattedLogValues(message, args), null, (state, ex) => state.ToString());
だからあなたはモックする必要があります
_loggerMock.Verify(logger => logger.Log(It.Is(LogLevel.Error), It.Is(0), It.IsAny<FormattedLogValues>(), It.IsAny<Exception>(), It.IsAny<Func<TState, Exception, string>>()), Times.Once);
免責事項私はコードを確認しませんでした
pinkfloydx33からのコメントの後で編集し、.net50でテスト例を設定し、次の答えに到達しました
最新のフレームワークでは、FormattedLogValuesクラスが内部に作成されています。したがって、これを一般的なMoq.Itメンバーで使用することはできません。しかし、Moqにはこれを行う別の方法があります(この回答は解決策にも言及しています)
このようなロガーへの呼び出しのために
_logger.LogError("myMessage");
このように確認する必要があります
_loggerMock.Verify(logger => logger.Log(
It.Is<LogLevel>(logLevel => logLevel == LogLevel.Error),
It.Is<EventId>(eventId => eventId.Id == 0),
It.Is<It.IsAnyType>((@object, @type) => @object.ToString() == "myMessage" && @type.Name == "FormattedLogValues"),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()),
Times.Once);
アクセスできないタイプにはIt.IsAnyTypeを使用します。また、検証を制限する必要がある場合は、func <object、type>を追加して、タイプが期待どおりかどうかを確認するか、パブリックインターフェイスにキャストして、パブリックメンバーを検証します。
文字列メッセージといくつかのパラメータを使用する場合は、FormattedLogValues型のオブジェクトをインターフェイスIReadOnlyList <KeyValuePair <string、object?>>にキャストし、さまざまなパラメータの文字列/値を確認する必要があります。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加