How do you mock ILogger LogInformation

匿名 (未验证) 提交于 2019-12-03 01:40:02

问题:

I have a class that receives an ILogger and I want to mock the LogInformation calls but this is an extension method. How do I make the appropiate setup call for this?

回答1:

ILogger is normally used thru extension methods, LogWarning, LogError, etc.

In my case I was interested in the LogWarning method which after looking at the code calls the Log method from ILogger. In order to mock it with Moq, this is what I ended up doing:

 var list = new List<string>();             var logger = new Mock<ILogger>();             logger                 .Setup(l => l.Log<FormattedLogValues>(LogLevel.Warning, It.IsAny<EventId>(), It.IsAny<FormattedLogValues>(), It.IsAny<Exception>(), It.IsAny<Func<FormattedLogValues, Exception, string>>()))                 .Callback(                 delegate (LogLevel logLevel, EventId eventId, FormattedLogValues state, Exception exception, Func<FormattedLogValues, Exception, string> formatter)                 {                     list.Add(state.ToString());                 });


回答2:

//Hi you can find the code with Moq.dll from the below link http://www.dotnetsurfers.com/blog/2010/04/02/getting-started-with-mocking-part-2-using-moq  //Here Define Interfaces with models Logger,Product and ShoppingCart  using System; using System.Diagnostics; using System.IO;  namespace MOQSamples.Model {     public interface ILogger     {         void Log(string text);     }      public class Logger : ILogger     {         public void Log(string text)         {             TextWriter tw = new StreamWriter(@"C:\temp\moq.log",false);             tw.WriteLine(text);             tw.Close();         }     } }     using System; using System.Collections.Generic; using System.Linq; using System.Text;  namespace MOQSamples.Model {     public class ShoppingCart     {         public ShoppingCart(ILogger logger)         {             this._logger = logger;         }          private ILogger _logger;         public decimal Total { get; set; }          public void AddProduct(IProduct product)         {             Total = Total + product.Price;             if (_logger != null)                 _logger.Log(String.Format("Product {0} has been added.",product.Name));         }     } }      using System; using System.Data.SqlClient;  namespace MOQSamples.Model {     public interface IProduct     {         string Name { get; set; }          decimal Price { get; set; }          string GetProductCategory();      }      public class Product : IProduct     {         public int ID {get;set;}          public string Name {get; set;}         public decimal Price         {             get { return GetPriceFromDatabase(); }             set { throw new NotImplementedException(); }         }          public string GetProductCategory()         {             throw new NotImplementedException();         }          private decimal GetPriceFromDatabase()         {             #region Retrieve Price from DB              var conn = new SqlConnection("Server=WIN-V0L52BJTJS6; Database=MOQ; Integrated Security=SSPI;");             var query = "select Price from Product where ID =" + ID;             var cmd = new SqlCommand(query, conn);             conn.Open();             var price = (decimal)cmd.ExecuteScalar();             conn.Close();             return price;              #endregion         }     } }  //testing the logger    using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Moq.Language; using MOQSamples.Model;   namespace MOQSamples.Test {     [TestClass]     public class MOQDemoTests     {         private Mock<IProduct> _mockProduct;         private Mock<ILogger> _mockLogger;          [TestInitialize]         public void InitializeTests()         {             _mockProduct = new Mock<IProduct>();             _mockLogger = new Mock<ILogger>();         }          [TestMethod]         public void Demo_Setup_Method()         {             //Show how a method call can be mocked and return fake data             _mockProduct.Setup(m => m.GetProductCategory()).Returns("Test Category");             Console.WriteLine(_mockProduct.Object.GetProductCategory());                  }          [TestMethod]         public void Demo_Setup_PropertyGet()         {             //Show how a property can be mocked and return fake data             _mockProduct.SetupGet(m => m.Name).Returns("Product 1");             Console.WriteLine(_mockProduct.Object.Name);         }          [TestMethod]         [ExpectedException(typeof(ArgumentNullException))]         public void Demo_Setup_ThrowException()         {             //show how a mock can be used to throw exception             _mockLogger.Setup(m => m.Log(It.Is<string>(p => p == null))).                 Throws(new ArgumentNullException());             _mockLogger.Object.Log(null);         }          [TestMethod]         public void Demo_Validate_Params()         {             //show how mock can validate parameters             _mockLogger.Setup(m => m.Log(It.IsRegex("[1-9]+"))).                 Callback(() => Console.WriteLine("Numbers passed"));             _mockLogger.Object.Log("123");         }          [TestMethod]         public void Demo_Verify_Interactions()         {             _mockLogger.Object.Log("test");             _mockLogger.Verify(m => m.Log(It.Is<string>(s=>s=="test")),Times.Once());         }          [TestMethod]         public void Demo_Setup_CallBack()         {              //show how a mock can be used to invoke a callback             int counter = 0;             _mockLogger.Setup(m => m.Log(It.IsAny<String>())).Callback(() => counter++);             _mockLogger.Object.Log("test");             _mockLogger.Object.Log("test2");              Console.WriteLine("Counter is " + counter);         }      } }


回答3:

This is how I workaround for Moq (v4.10.1) framework.

public static class TestHelper {       public static Mock<ILogger<T>> GetMockedLoggerWithAutoSetup<T>()     {         var logger = new Mock<ILogger<T>>();          logger.Setup<object>(x => x.Log(        It.IsAny<LogLevel>(),        It.IsAny<EventId>(),        It.IsAny<object>(),        It.IsAny<Exception>(),        It.IsAny<Func<object, Exception, string>>()));          return logger;     }      public static void VerifyLogMessage<T>(Mock<ILogger<T>> mockedLogger, LogLevel logLevel, Func<string, bool> predicate, Func<Times> times)     {         mockedLogger.Verify(x => x.Log(logLevel, 0, It.Is<object>(p => predicate(p.ToString())), null, It.IsAny<Func<object, Exception, string>>()), times);     } }

--

public class Dummy {  }  [Fact] public void Should_Mock_Logger() {     var logger = TestHelper.GetMockedLoggerWithAutoSetup<Dummy>();     logger.Object.LogInformation("test");     TestHelper.VerifyLogMessage<Dummy>(logger, LogLevel.Information, msg => msg == "test", Times.Once); }

--

The thing is,

If I had chosen any other <TCustom> than <object> for logger.Setup(), it would fail on Verify step saying that 0 calls were made for x.Log<TCustom> and showing a call made to x.Log<object>. So I setup my generic logger to mock Log<object>(..) method instead.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!