2017-07-20 18 views
6

Tôi đang cố gắng để viết một số xét nghiệm đơn vị cho hành động điều khiển. Để làm điều đó, tôi đang sử dụng XUnit và Moq. Các bộ điều khiển có một ILoggerFactory tiêm trong constructor. Làm thế nào để Moq này lên để thử nghiệm?Làm thế nào để Moq Mock một LoggerFactory trong C# AspNet Core

Tôi đã thử chế nhạo một Logger cho lớp điều khiển, và sau đó chế nhạo CreateLogger để trả về trình ghi logger, nhưng tôi tiếp tục nhận được nhiều lần kiểm tra NullReferenceExceptions khi hàm LogInformation() được gọi.

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

Tôi cho rằng vấn đề là LogInformation đang được gọi, và đây là phương pháp mở rộng, vì vậy làm thế nào để moq?

+1

Chúng tôi thường tiêm 'ILogger ' vào bộ điều khiển. Lý do đằng sau việc tiêm 'ILoggerFactory' là gì? – Win

+0

Tôi có thể thử. Nó sẽ không còn sử dụng phương pháp mở rộng và do đó vẫn thất bại? –

+0

Nó hoạt động! Nếu tôi thay đổi đối tượng được tiêm vào ILogger , tôi có thể giả lập trình ghi với "var mockLogger = new Mock >();" và sau đó vượt qua mockLogger.Object vào bộ điều khiển thử nghiệm. Nếu bạn @Win đăng câu trả lời, tôi sẽ rất vui khi được chấp nhận. –

Trả lời

0

Đối với bất cứ ai cần một câu trả lời cho câu hỏi này, chứ không phải là một công việc xung quanh, tôi đã mở rộng công việc của bài viết này:

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

Bọc bất kỳ một phần của khung đăng nhập mà bạn sử dụng, đó là một ý tưởng hay. Bắt đầu với giao diện đăng nhập của riêng bạn:

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

Sau đó thêm việc thực hiện để vượt qua thông qua các cuộc gọi đến khuôn khổ:

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

Di chuyển trên, thêm một giao diện cho gói nhà máy logger:

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

Và triển khai:

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

Đây là những nơi duy nhất bạn nên tham khảo không gian tên Microsoft.Extensions.Logging. Ở những nơi khác, hãy sử dụng ILog<T> thay vì ILogger<T>ILogFactory thay vì ILoggerFactory của bạn. Nơi bạn sẽ thường phụ thuộc tiêm LoggerFactory, thay vì tiêm wrapper:

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

Trong mã chính của bạn, bạn có thể lấy LogFactory này và tạo cụ thể Log<T> của bạn cho lớp học của bạn:

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

Bạn có thể tưởng tượng thay đổi thông số của MyMethod từ IServiceCollection đến ILogFactory hoặc ILog<MyClass> theo yêu cầu. Và - toàn bộ vấn đề là - bây giờ bạn có thể thử các mã trên với:

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

"Tùy thuộc vào loại bạn không kiểm soát suốt ứng dụng của bạn thêm khớp nối và thường xuyên trở thành một nguồn của vấn đề và nợ kỹ thuật." - Steve Smith

Các vấn đề liên quan