How to unit test with ILogger in ASP.NET Core

后端 未结 14 1188
南旧
南旧 2020-12-04 11:47

This is my controller:

public class BlogController : Controller
{
    private IDAO _blogDAO;
    private readonly ILogger _         


        
14条回答
  •  抹茶落季
    2020-12-04 12:09

    I have created a package, Moq.ILogger, to make testing ILogger extensions much easier.

    You can actually use something like the following which is more close to your actual code.

    loggerMock.VerifyLog(c => c.LogInformation(
                     "Index page say hello", 
                     It.IsAny());
    

    Not only it is easier to write new tests, but also the maintenance is with no costs.

    The repo can be found here and there is a nuget package too (Install-Package ILogger.Moq).

    I explained it also with a real-life example on my blog.

    In short, let's say if you have the following code:

    public class PaymentsProcessor
    {
        private readonly IOrdersRepository _ordersRepository;
        private readonly IPaymentService _paymentService;
        private readonly ILogger _logger;
    
        public PaymentsProcessor(IOrdersRepository ordersRepository, 
            IPaymentService paymentService, 
            ILogger logger)
        {
            _ordersRepository = ordersRepository;
            _paymentService = paymentService;
            _logger = logger;
        }
    
        public async Task ProcessOutstandingOrders()
        {
            var outstandingOrders = await _ordersRepository.GetOutstandingOrders();
            
            foreach (var order in outstandingOrders)
            {
                try
                {
                    var paymentTransaction = await _paymentService.CompletePayment(order);
                    _logger.LogInformation("Order with {orderReference} was paid {at} by {customerEmail}, having {transactionId}", 
                                           order.OrderReference, 
                                           paymentTransaction.CreateOn, 
                                           order.CustomerEmail, 
                                           paymentTransaction.TransactionId);
                }
                catch (Exception e)
                {
                    _logger.LogWarning(e, "An exception occurred while completing the payment for {orderReference}", 
                                       order.OrderReference);
                }
            }
            _logger.LogInformation("A batch of {0} outstanding orders was completed", outstandingOrders.Count);
        }
    }
    

    You could then write some tests like

    [Fact]
    public async Task Processing_outstanding_orders_logs_batch_size()
    {
        // Arrange
        var ordersRepositoryMock = new Mock();
        ordersRepositoryMock.Setup(c => c.GetOutstandingOrders())
            .ReturnsAsync(GenerateOutstandingOrders(100));
    
        var paymentServiceMock = new Mock();
        paymentServiceMock
            .Setup(c => c.CompletePayment(It.IsAny()))
            .ReturnsAsync((Order order) => new PaymentTransaction
            {
                TransactionId = $"TRX-{order.OrderReference}"
            });
    
        var loggerMock = new Mock>();
    
        var sut = new PaymentsProcessor(ordersRepositoryMock.Object, paymentServiceMock.Object, loggerMock.Object);
    
        // Act
        await sut.ProcessOutstandingOrders();
    
        // Assert
        loggerMock.VerifyLog(c => c.LogInformation("A batch of {0} outstanding orders was completed", 100));
    }
    
    [Fact]
    public async Task Processing_outstanding_orders_logs_order_and_transaction_data_for_each_completed_payment()
    {
        // Arrange
        var ordersRepositoryMock = new Mock();
        ordersRepositoryMock.Setup(c => c.GetOutstandingOrders())
            .ReturnsAsync(GenerateOutstandingOrders(100));
    
        var paymentServiceMock = new Mock();
        paymentServiceMock
            .Setup(c => c.CompletePayment(It.IsAny()))
            .ReturnsAsync((Order order) => new PaymentTransaction
            {
                TransactionId = $"TRX-{order.OrderReference}"
            });
    
        var loggerMock = new Mock>();
    
        var sut = new PaymentsProcessor(ordersRepositoryMock.Object, paymentServiceMock.Object, loggerMock.Object);
    
        // Act
        await sut.ProcessOutstandingOrders();
    
        // Assert
        loggerMock.VerifyLog(logger => logger.LogInformation("Order with {orderReference} was paid {at} by {customerEmail}, having {transactionId}",
            It.Is(orderReference => orderReference.StartsWith("Reference")),
            It.IsAny(),
            It.Is(customerEmail => customerEmail.Contains("@")),
            It.Is(transactionId => transactionId.StartsWith("TRX"))),
          Times.Exactly(100));
    }
    
    [Fact]
    public async Task Processing_outstanding_orders_logs_a_warning_when_payment_fails()
    {
        // Arrange
        var ordersRepositoryMock = new Mock();
        ordersRepositoryMock.Setup(c => c.GetOutstandingOrders())
            .ReturnsAsync(GenerateOutstandingOrders(2));
    
        var paymentServiceMock = new Mock();
        paymentServiceMock
            .SetupSequence(c => c.CompletePayment(It.IsAny()))
            .ReturnsAsync(new PaymentTransaction
            {
                TransactionId = "TRX-1",
                CreateOn = DateTime.Now.AddMinutes(-new Random().Next(100)),
            })
            .Throws(new Exception("Payment exception"));
    
        var loggerMock = new Mock>();
    
        var sut = new PaymentsProcessor(ordersRepositoryMock.Object, paymentServiceMock.Object, loggerMock.Object);
    
        // Act
        await sut.ProcessOutstandingOrders();
    
        // Assert
        loggerMock.VerifyLog(c => c.LogWarning(
                     It.Is(paymentException => paymentException.Message.Contains("Payment exception")), 
                     "*exception*Reference 2"));
    }
    

提交回复
热议问题