2017-07-20 1 views
6

Ich versuche, einige Komponententests für Controller-Aktionen zu schreiben. Um das zu tun, verwende ich XUnit und Moq. Die Controller haben eine ILoggerFactory im Konstruktor eingefügt. Wie geht es einem Moq zum Testen?Wie Moq Mock eine LoggerFactory in C# AspNet Core

Ich habe versucht, einen Logger für die Controller-Klasse zu verspotten und dann CreateLogger verspotten, um die Mock Logger zurückzugeben, aber ich bekomme verschiedene Test Runtime NullReferenceExceptions, wenn die LogInformation() Funktion aufgerufen wird.

 // 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); 

Ich nehme an, das Problem ist, dass LogInformation genannt wird, und dies ist eine Erweiterung Methode, so wie man das moq?

+1

Normalerweise injizieren wir 'ILogger ' zum Controller. Was ist der Grund für die Injektion von "ILoggerFactory"? – Win

+0

Ich kann es versuchen. Wird es die Erweiterungsmethode nicht verwenden und daher immer noch fehlschlagen? –

+0

Es funktioniert! Wenn ich das injizierte Objekt in ILogger ändere, kann ich den Logger mit "var mockLogger = new Mock >();" und dann mockLogger.Object in den Test-Controller übergeben. Wenn du @Win eine Antwort postest, würde ich mich freuen zu akzeptieren. –

Antwort

0

Für alle, eine Antwort auf diese Frage benötigen, anstatt eine Arbeit um, erweiterte ich die Arbeit dieses Artikels:

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

Wrap ein Teil des Logging-Framework, die Sie verwenden, die a gute Idee sowieso. Beginnen Sie mit Ihrem eigenen Logging-Schnittstelle:

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

dann die Implementierung hinzuzufügen durch Aufrufe an den Rahmen weitergeben müssen:

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); 
} 

Umzug auf, fügen Sie eine Schnittstelle zum Einwickeln des Loggers Fabrik:

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

Und die Umsetzung:

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)); 
} 

Dies sind die einzigen Stellen, an denen Sie auf den Namespace Microsoft.Extensions.Logging verweisen sollten. Verwenden Sie andernfalls ILog<T> anstelle von ILogger<T> und ILogFactory anstelle von ILoggerFactory. Wo würden Sie normalerweise Abhängigkeit LoggerFactory injizieren, spritzen, statt den Wrapper:

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

In Ihrem Haupt-Code können Sie diese LogFactory abrufen und erstellen Sie Ihre spezifischen Log<T> für Ihre Klasse:

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!"); 
    } 
} 

Sie sich vorstellen, die sich wandelnde kann Parameter von MyMethod von IServiceCollection zu ILogFactory oder ein ILog<MyClass> wie erforderlich. Und - der springende Punkt ist - Sie jetzt den obigen Code mit verspotten:

[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); 
} 

„Je nach Typen, die Sie haben keine Kontrolle der gesamten Anwendung Kopplung ergänzt und wird häufig eine Quelle von Problemen und technischen Schulden.“ - Steve Smith