I have a method which has a line as follows:
_logger.LogError(exception, $"Encountered {exception.GetType().Name}. Unable to verify user with id {user.UserId}");
This has a corresponding unit test with the following assertion:
var logger = Substitute.For<ILogger<SyncService>>();
// other Arrange, Act, Assert steps
logger.Received(1).LogError(exception, "Encountered NullReferenceException. Unable to verify user with id 1");
This test was running fine.
However, due to some issues we encountered, this log now needs to be converted into a structured log.
So now the line in the class looks as follows:
_logger.LogError(exception, "Encountered {exceptionType}. Unable to verify user with id {userId}", exception.GetType().Name, user.UserId);
But now when I changed the assertion to the following the test fails:
logger.Received(1).LogError(exception, "Encountered {exceptionType}. Unable to verify user with id {userId}", "NullReferenceException", 1);
The error message is as follows. I have removed the unwanted stack traces to highlight only the important parts:
NSubstitute.Exceptions.ReceivedCallsException : Expected to receive exactly 1 call matching:
Log<FormattedLogValues>(Error, 0, Encountered NullReferenceException. Unable to verify user with id 1, System.NullReferenceException: Test Exception
at NSubstitute.ExceptionExtensions.ExceptionExtensions.<>c__DisplayClass0_0.<Throws>b__0(CallInfo _)
...// complete stack trace ...
Actually received no matching calls.
Received 1 non-matching call (non-matching arguments indicated with '*' characters):
Log<FormattedLogValues>(Error, 0, *Encountered NullReferenceException. Unable to verify user with id 1*, System.NullReferenceException: Test Exception
at NSubstitute.ExceptionExtensions.ExceptionExtensions.<>c__DisplayClass0_0.<Throws>b__0(CallInfo _)
I was initially unable to figure out what I am doing incorrectly. From the 2 messages it seems like the correct method is being called with the correct parameters, but the message is still flagged as non-matching.
But after some digging I came to realize the flagged message is actually a ToString()
call to FormattedLogValues
which happens when the exception is thrown. Internally, it is trying to compare an instance of string
to an instance of FormattedLogValues
I tried to directly assert on Log<FormattedLogValues>
but it seems like the class FormattedLogValues
is not available for external use.
This is how the issue was resolved earlier: https://github.com/nsubstitute/NSubstitute/issues/384
But now the struct FormattedLogValues
is no longer available for public use. There is an open issue regarding this here: https://github.com/dotnet/runtime/issues/67577
But now the question is, how do I test this? I know Moq has a method called It.IsAnyType()
which can be used to ignore the type of the message template, but does NSubstitute have anything similar?
I have seen some other posts in StackOverflow with similar issues but the answers do not seem to work when structured logs are used