2017-07-20 28 views
6

Próbuję napisać kilka testów jednostkowych dla działań kontrolera. Aby to zrobić, używam XUnit i Moq. Kontrolery mają wbudowany ILoggerFactory w konstruktorze. W jaki sposób jeden Moq jest gotowy do testowania?Jak Moq Mock a LoggerFactory w C# AspNet Core

Próbowałem kpić z Loggera dla klasy kontrolera, a następnie kpić z CreateLoggera, aby zwrócić fałszywego Loggera, ale wciąż otrzymuję różne testy NullReferenceExceptions, gdy wywoływana jest funkcja LogInformation().

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

Zakładam, że problemem jest to, że LogInformation jest nazywany, i jest to metoda rozszerzenie, tak jak Min, że?

+1

Zazwyczaj wprowadzamy sterownik "ILogger ". Jaki jest powód wstrzyknięcia "ILoggerFactory"? – Win

+0

Mogę spróbować. Czy nadal nie będzie korzystać z metody rozszerzenia, a zatem nadal nie będzie działać? –

+0

To działa! Jeśli zmienię wstrzyknięty obiekt na ILogger , mogę wyśmiać rejestrator za pomocą "var mockLogger = new Mock >();" a następnie przekazać mockLogger.Object do kontrolera testowego. Jeśli @Win opublikujesz odpowiedź, z przyjemnością ją zaakceptuję. –

Odpowiedz

0

Dla każdego, która chciałaby odpowiedzieć na to pytanie, a nie jako pracę wokół, I przedłużony pracę tego artykułu:

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

Wrap jakakolwiek część ram rejestrowania, że ​​używasz, co jest dobry pomysł. Start z własnym interfejsem logowania:

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

Następnie dodać realizację przekazać za pośrednictwem zaproszeń do ram:

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

Idąc dalej, dodać interfejs do owijania fabrykę Rejestrator:

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

I realizacja:

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

Są to jedyne miejsca, w których należy odwoływać się do przestrzeni nazw Microsoft.Extensions.Logging. W innym miejscu użyj numeru ILog<T> zamiast ILogger<T> i swojego ILogFactory zamiast ILoggerFactory. Gdzie normalnie można uzależnienie wstrzyknąć LoggerFactory, zamiast wstrzykiwać opakowanie:

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

w głównym kodu można pobrać tę LogFactory i stworzyć swój specyficzny Log<T> dla swojej klasy:

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

można sobie wyobrazić zmieniając parametr MyMethod od IServiceCollection do ILogFactory lub ILog<MyClass> zgodnie z wymaganiami. I - cały punkt jest - można teraz drwić powyższy kod z:

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

„W zależności od rodzaju, że nie kontrolują całej aplikacji dodaje sprzęgło i często staje się źródłem problemów i długu technicznego.” - Steve Smith