2017-07-20 6 views
6

私はコントローラアクションのためのいくつかの単体テストを書こうとしています。これを行うために、私はXUnitとMoqを使用しています。コントローラにはコンストラクタにILoggerFactoryが注入されています。 Moqはこれをどのようにテストするのですか?C#AspNetコアでLoggerFactoryをモックする方法

私はコントローラクラス用のLoggerを嘲笑してから、Create LoggerをモックLoggerを返すようにモックアップしましたが、LogInformation()関数が呼び出されると、さまざまなテストランタイムNullReferenceExceptionsを取得し続けます。

 // Logger that yields only disappointment...   
     var mockLogger = new Mock<ILogger<JwtController>>(); 
     mockLogger.Setup(ml => ml.Log(It.IsAny<LogLevel>(), It.IsAny<EventId>(), It.IsAny<object>(), It.IsAny<Exception>(), It.IsAny<Func<object, Exception, string>>())); 
     var mockLoggerFactory = new Mock<ILoggerFactory>(); 
     mockLoggerFactory.Setup(mlf => mlf.CreateLogger("JwtController")).Returns(mockLogger.Object); 

私はこの問題は、ログ情報が呼び出されていることであり、これは拡張メソッドなので、それをMOQする方法と仮定しますか?

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

ラップ使用するロギングフレームワークの一部です:

+1

通常、ILogger をコントローラに注入します。 'ILoggerFactory'を注入する理由は何ですか? – Win

+0

私はそれを試すことができます。まだ拡張メソッドを使用していないので、まだ失敗しますか? –

+0

それは動作します!注入されたオブジェクトをILogger に変更すると、 "var mockLogger = new Mock >();" mockLogger.Objectをテストコントローラに渡します。あなたが@Winが回答を投稿した場合、私は受け入れることができます。 –

答えて

0

は周りこの質問への答えではなく、仕事を必要とする誰のために、私はこの記事の作業を延長しましたとにかく良いアイデア。独自のログ・インターフェースを開始します。

public interface ILog<T> 
{ 
    void LogError(Exception ex, string message, params object[] args); 
    void LogInformation(string message, params object[] args); 
} 

そして、フレームワークへの呼び出しを通過するように実装を追加:

public class Log<T> : ILog<T> 
{ 
    private readonly ILogger<T> logger; 

    public Log(ILogger<T> logger) 
    { 
     this.logger = logger; 
    } 

    public void LogError(Exception ex, string message, params object[] args) => this.logger.LogError(ex, message, args); 
    public void LogInformation(string message, params object[] args) => this.logger.LogInformation(message, args); 
} 

は上を移動する、ロガー工場を包むためのインターフェイスを追加します。

public interface ILogFactory 
{ 
    ILog<T> CreateLog<T>(); 
} 

実装:

public class LogFactory : ILogFactory 
{ 
    private readonly ILoggerFactory loggerFactory; 

    public LogFactory() 
    { 
     this.loggerFactory = new LoggerFactory(); 
    } 

    public ILog<T> CreateLog<T>() => new Log<T>(new Logger<T>(this.loggerFactory)); 
} 

これは、Microsoft.Extensions.Logging名前空間を参照する必要がある唯一の場所です。それ以外の場合は、ILogger<T>の代わりにILog<T>を、ILoggerFactoryの代わりにILogFactoryを使用してください。あなたの主なコードで

IServiceCollection serviceCollection = new ServiceCollection(); 
serviceCollection.AddSingleton<ILogFactory>(new LogFactory()); 

あなたはこのLogFactoryを取得し、あなたのクラスの特定のLog<T>を作成することができます:あなたは、通常、依存関係の代わりにラッパーを注入、LoggerFactoryを注入する場所

public class MyClass 
{ 
    public void MyMethod(IServiceCollection serviceCollection) 
    { 
     var serviceProvider = serviceCollection.BuildServiceProvider(); 
     var logFactory = this.serviceProvider.GetRequiredService<ILogFactory>(); 
     var log = logFactory.CreateLog<ServiceApplication>(); 
     log.LogInformation("Hello, World!"); 
    } 
} 

あなたが変えることを想像することができますパラメータは、MyMethodIServiceCollectionからILogFactoryまたはILog<MyClass>から必要に応じて変更できます。そして - 全体のポイントがある - あなたは今で上記のコードを模擬することができます

[Fact] 
public void Test() 
{ 
    IServiceCollection serviceCollection = new ServiceCollection(); 
    var mockLog = new Mock<ILog<MyClass>>(); 
    var mockLogFactory = new Mock<ILogFactory>(); 
    mockLogFactory.Setup(f => f.CreateLog<MyClass>()).Returns(mockLog.Object); 
    serviceCollection.AddSingleton<ILogFactory>(mockLogFactory.Object); 

    var myClass = new MyClass(); 
    myClass.MyMethod(serviceCollection); 

    mockLog.Verify(l => l.LogInformation("Hello, World!"), Times.Once); 
} 

「あなたはあなたのアプリケーション全体でコントロールしていない種類に応じてカップリングを追加し、頻繁に問題と技術的負債のソースになります。」 - Steve Smith