From f5563485be10b393ee745c90a88b0ec2c43d9153 Mon Sep 17 00:00:00 2001 From: Chris Winland Date: Thu, 27 Jun 2024 12:32:51 -0400 Subject: [PATCH] Add VerifyLogger --- .../Extensions/TestClassExtensions.cs | 128 +++++++++++++++++- FastMoq.Tests/MocksTests.cs | 55 ++++++++ 2 files changed, 181 insertions(+), 2 deletions(-) diff --git a/FastMoq.Core/Extensions/TestClassExtensions.cs b/FastMoq.Core/Extensions/TestClassExtensions.cs index 8d2b1b5..5608986 100644 --- a/FastMoq.Core/Extensions/TestClassExtensions.cs +++ b/FastMoq.Core/Extensions/TestClassExtensions.cs @@ -1,6 +1,7 @@ using FastMoq.Models; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Moq; using System.Collections; using System.Linq.Expressions; using System.Reflection; @@ -12,7 +13,7 @@ namespace FastMoq.Extensions { /// - /// Class TestClassExtensions. + /// Helper Methods for testing. /// public static class TestClassExtensions { @@ -380,6 +381,129 @@ public static void SetFieldValue(this TObject obj, string name, object? public static void SetPropertyValue(this TObject obj, string name, object? value) where TObject : class? => obj.GetProperty(name)?.SetValue(obj, value); + /// + /// Verifies the Mock ILogger was invoked. + /// + /// The logger mock. + /// The expected log level. + /// The expected message. + /// The expected number of invocations. + public static void VerifyLogger(this Mock loggerMock, LogLevel logLevel, string message, int times = 1) => loggerMock.VerifyLogger(logLevel, message, null, null, times); + + /// + /// Verifies the Mock ILogger of T was invoked. + /// + /// + /// The logger mock. + /// The expected log level. + /// The expected message. + /// The expected number of invocations. + public static void VerifyLogger(this Mock> loggerMock, LogLevel logLevel, string message, int times = 1) => loggerMock.VerifyLogger(logLevel, message, null, null, times); + + /// + /// Verifies the Mock ILogger was invoked. + /// + /// The logger mock. + /// The log level. + /// The message. + /// The exception. + /// The event identifier. + /// The expected number of invocations. + public static void VerifyLogger(this Mock loggerMock, LogLevel logLevel, string message, Exception? exception, int? eventId = null, + int times = 1) => loggerMock.VerifyLogger(logLevel, message, exception, eventId, times); + + /// + /// Verifies the Mock ILogger was invoked. + /// + /// The logger mock. + /// The log level. + /// The message. + /// The exception. + /// The event identifier. + /// The expected number of invocations. + public static void VerifyLogger(this Mock> loggerMock, LogLevel logLevel, string message, Exception? exception, int? eventId = null, + int times = 1) => loggerMock.VerifyLogger(logLevel, message, exception, eventId, times); + + /// + /// Verifies the Mock ILogger was invoked. + /// + /// The type of the exception. + /// The logger mock. + /// The log level. + /// The message. + /// The exception. + /// The event identifier. + /// The expected number of invocations. + public static void VerifyLogger(this Mock loggerMock, LogLevel logLevel, string message, TException? exception, int? eventId = null, int times = 1) + where TException : Exception + => loggerMock.Verify(TestLoggerExpression(logLevel, message, exception, eventId), Times.Exactly(times)); + + /// + /// Verifies the Mock ILogger was invoked. + /// + /// The type of the exception. + /// The type of ILogger. + /// The logger mock. + /// The log level. + /// The message. + /// The exception. + /// The event identifier. + /// The expected number of invocations. + public static void VerifyLogger(this Mock> loggerMock, LogLevel logLevel, string message, TException? exception, int? eventId = null, int times = 1) + where TException : Exception + => loggerMock.Verify(TestLoggerExpression>(logLevel, message, exception, eventId), Times.Exactly(times)); + + /// + /// Tests the logger expression2. + /// + /// The type of the exception. + /// The ILogger type. + /// The log level. + /// The message. + /// The exception. + /// The event identifier. + /// System.Linq.Expressions.Expression<System.Action<T>>. + internal static Expression> TestLoggerExpression(LogLevel logLevel, string message, TException? exception, + int? eventId) where TException : Exception where TLoggerType : ILogger => + logger => + logger.Log( + logLevel, + It.Is(e => CheckEventId(e, eventId)), + It.Is((o, t) => CheckMessage(o.ToString() ?? string.Empty, t, message, t)), + It.Is(e => CheckException(e, exception)), + It.IsAny>()); + + /// + /// Checks the expectedMessage. + /// + /// The object. + /// The type. + /// The expected expectedMessage. + /// The expected type. + /// System.Boolean. + internal static bool CheckMessage(string verifyMessage, Type type, string expectedMessage, Type expectedType) => + verifyMessage.Contains(expectedMessage, StringComparison.OrdinalIgnoreCase) && + type.IsAssignableTo(expectedType); + + /// + /// Checks the event identifier. + /// + /// The event identifier. + /// The expected event identifier. + /// System.Boolean. + internal static bool CheckEventId(EventId verifyEventId, int? eventId) => eventId == null || verifyEventId == eventId; + + /// + /// Checks the expectedException. + /// + /// The expectedException. + /// The expected expectedException. + /// System.Boolean. + internal static bool CheckException(Exception verifyException, Exception? expectedException) => expectedException == null || + (verifyException.Message.Contains( + expectedException.Message, StringComparison.OrdinalIgnoreCase) && + verifyException.GetType().IsAssignableTo(expectedException.GetType())); + /// /// ForEach for . /// @@ -468,7 +592,7 @@ internal static MemberExpression GetMemberExpressionInternal(this Expression met /// The type to try to create. /// The constructors to test with the specified type. /// List<FastMoq.Models.ConstructorModel>. - internal static List GetTestedConstructors(this Mocker mocker, Type type, List constructors) + internal static List GetTestedConstructors(this Mocker mocker, Type type, List? constructors) { constructors ??= new(); var validConstructors = new List(); diff --git a/FastMoq.Tests/MocksTests.cs b/FastMoq.Tests/MocksTests.cs index b1d404e..498aa48 100644 --- a/FastMoq.Tests/MocksTests.cs +++ b/FastMoq.Tests/MocksTests.cs @@ -1028,6 +1028,61 @@ public void CallMethod_WithException() Assert.Throws(() => Mocks.CallMethod(CallTestMethod, 4, null)); } + [Fact] + public void VerifyLogger_ShouldPass_WhenMatches() + { + var mLogger = new Mock(); + mLogger.VerifyLogger(LogLevel.Information, "test", 0); + + mLogger.Object.LogInformation("test"); + mLogger.VerifyLogger(LogLevel.Information, "test"); + + mLogger.Object.LogInformation("test"); + mLogger.VerifyLogger(LogLevel.Information, "test", 2); + mLogger.VerifyLogger(LogLevel.Information, "test", null, null, 2); + + mLogger.Invocations.Clear(); + mLogger.Object.LogError(1, new AmbiguousImplementationException("Test Exception"), "test message"); + mLogger.VerifyLogger(LogLevel.Error, "test", new AmbiguousImplementationException("Test Exception"), 1); + mLogger.VerifyLogger(LogLevel.Error, "test", new AmbiguousImplementationException("Test Exception"), 1); + } + + [Fact] + public void VerifyLogger_ShouldPass_WhenMatchesILoggerSubtype() + { + var mLogger = new Mock>(); + mLogger.VerifyLogger(LogLevel.Information, "test", 0); + + mLogger.Object.LogInformation("test"); + mLogger.VerifyLogger(LogLevel.Information, "test"); + + mLogger.Object.LogInformation("test"); + mLogger.VerifyLogger(LogLevel.Information, "test", 2); + mLogger.VerifyLogger(LogLevel.Information, "test", null, null, 2); + + mLogger.Invocations.Clear(); + mLogger.Object.LogError(1, new AmbiguousImplementationException("Test Exception"), "test message"); + mLogger.VerifyLogger(LogLevel.Error, "test", new AmbiguousImplementationException("Test Exception"), 1); + mLogger.VerifyLogger(LogLevel.Error, "test", new AmbiguousImplementationException("Test Exception"), 1); + } + + [Fact] + public void VerifyLogger_ShouldThrow_WhenNotMatches() + { + var mLogger = new Mock(); + mLogger.VerifyLogger(LogLevel.Information, "test", 0); + + mLogger.Object.LogInformation("test"); + Assert.Throws(() => mLogger.VerifyLogger(LogLevel.Information, "test2")); // Wrong Message. + + mLogger.Object.LogInformation("test"); + Assert.Throws(() => mLogger.VerifyLogger(LogLevel.Information, "test")); // Wrong number of times. + + mLogger.Invocations.Clear(); + mLogger.Object.LogError(1, new AmbiguousImplementationException("Test Exception"), "test message"); + Assert.Throws(() => mLogger.VerifyLogger(LogLevel.Error, "test", new AmbiguousImplementationException("Test Exception"), 0)); // Wrong eventId. + } + private static void LogException(Exception ex, ILogger log, string customMessage = "", [CallerMemberName] string caller = "") { log.LogError("[{caller}] - {customMessage}{errorMessage}", caller, $"{customMessage} ", ex.Message);