Last active
April 28, 2021 11:50
-
-
Save MelbourneDeveloper/8b6eeda620ee74ad17b30f1ab1407049 to your computer and use it in GitHub Desktop.
ILogger Verification with Moq
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Extensions.Logging; | |
using Moq; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
// More detailed samples here: | |
// https://github.com/MelbourneDeveloper/RestClient.Net/blob/79cca66e02f83a1043c44f215d374139f40f8c12/src/RestClient.Net.UnitTests/UnitTests.cs#L758 | |
namespace RestClient.Net.UnitTests | |
{ | |
public static class LogCheckExtensions | |
{ | |
/// <summary> | |
/// Verify that the log was called and get access to check the log arguments | |
/// </summary> | |
public static void VerifyLog<T, TException>( | |
this Mock<ILogger<T>> loggerMock, | |
Expression<Func<object, Type, bool>> match, | |
LogLevel logLevel, | |
int times) where TException : Exception | |
{ | |
if (loggerMock == null) throw new ArgumentNullException(nameof(loggerMock)); | |
loggerMock.Verify | |
( | |
l => l.Log | |
( | |
//Check the severity level | |
logLevel, | |
//This may or may not be relevant to your scenario | |
//If you need to check this, add a parameter for it | |
It.IsAny<EventId>(), | |
//This is the magical Moq code that exposes internal log processing from the extension methods | |
It.Is<It.IsAnyType>(match), | |
//Confirm the exception type | |
It.IsAny<TException>(), | |
//Accept any valid Func here. The Func is specified by the extension methods | |
(Func<It.IsAnyType, Exception, string>)It.IsAny<object>() | |
), | |
//Make sure the message was logged the correct number of times | |
Times.Exactly(times) | |
); | |
} | |
/// <summary> | |
/// Verify that the log was called and get access to check the log arguments | |
/// </summary> | |
public static void VerifyLog<T>( | |
this Mock<ILogger<T>> loggerMock, | |
Expression<Func<object, Type, bool>> match, | |
LogLevel logLevel, | |
int times) | |
=> VerifyLog<T, Exception>(loggerMock, match, logLevel, times); | |
/// <summary> | |
/// Check whether or not the log arguments match the expected result | |
/// </summary> | |
public static bool CheckValue<T>(this object state, string key, T expectedValue) | |
=> CheckValue<T>(state, key, (actualValue) | |
=> (actualValue == null && expectedValue == null) || (actualValue != null && actualValue.Equals(expectedValue))); | |
/// <summary> | |
/// Check whether or not the log arguments match the expected result | |
/// </summary> | |
public static bool CheckValue<T>(this object state, string key, Func<T, bool> compare) | |
{ | |
if (compare == null) throw new ArgumentNullException(nameof(compare)); | |
var exists = state.GetValue<T>(key, out var actualValue); | |
return exists && compare(actualValue); | |
} | |
#pragma warning disable CA1021 // Avoid out parameters | |
public static bool GetValue<T>(this object state, string key, out T value) | |
#pragma warning restore CA1021 // Avoid out parameters | |
{ | |
var keyValuePairList = (IReadOnlyList<KeyValuePair<string, object>>)state; | |
var keyValuePair = keyValuePairList.FirstOrDefault(kvp => string.Compare(kvp.Key, key, StringComparison.Ordinal) == 0); | |
value = (T)keyValuePair.Value; | |
return keyValuePair.Key != null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment