docs icon indicating copy to clipboard operation
docs copied to clipboard

Beware when mocking in unit tests!

Open ArticadAlistair opened this issue 3 years ago • 6 comments

When one's logging class implements an interface, and that interface is used to make a mock for unit testing, the addition of the "public void LogMessage(LogLevel level, LogInterpolatedStringHandler builder)" method prevents a mock from being fully implemented. This is because the LogInterpolatedStringHandler is a ref struct, and mocking frameworks will not allow ref structs to be used as arguments to mocked methods. This could be a serious problem for some people. I solved it in my case by replacing all mocked instances with the real thing, which was about 2 hours' work, and left my unit tests dependent on another class, which I didn't appreciate.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

ArticadAlistair avatar Aug 02 '22 13:08 ArticadAlistair

Hi @ArticadAlistair

I'd like to learn more about how you got to the state where you added a custom interpolated string handler in a class that needed mocking.

I hadn't considered the case where those would be combined. First, I expect that most .NET developers will use the interpolated string handlers provided by the .NET runtime. Second, I expected that custom handlers would only be created for performance reasons where not creating the output string, or truncating the output could save important machine cycles, I'd like to know more about what I missed in that analysis.

BillWagner avatar Aug 02 '22 15:08 BillWagner

First I created my own class for logging. Several other classes use this, but I wanted to abstract logging out when unit testing those other classes. So I created an interface for the logging class, and mocked it in the unit tests. Subsequently, I read about the ability to implement an interpolated string handler and decided it would be a good performance improvement for my logging class. Then reality hit - all those unit tests started to fall apart when I tried (and failed) to mock the new method that needs the interpolated string handler ref struct as an argument. I fixed the tests by using the real logging class in all the tests; I couldn't see any other way. You could argue that if the logging class is adequately tested then this is not a big deal. The big deal is that I didn't anticipate this problem and got to a place where it either took a long time to fix or else I had to abandon the interpolated string handler.

ArticadAlistair avatar Aug 02 '22 16:08 ArticadAlistair

If you're using the ILogger (or ILogger<T>) abstractions, you should consider using the NullLogger.Instance for unit testing. For more information, see Logging in .NET.

IEvangelist avatar Aug 02 '22 20:08 IEvangelist

I've written my own interface. (When I read up on what's already out there, I found it somewhat overwhelming, and yet nothing seemed to do exactly what I needed.) But in any case, my unit tests of classes that use logging needed to verify that error conditions had been communicated to the logger, so a null logger wouldn't really help with that.

ArticadAlistair avatar Aug 03 '22 08:08 ArticadAlistair

I didn't realize that this was related to this tutorial:

https://docs.microsoft.com/dotnet/csharp/whats-new/tutorials/interpolated-string-handler

This isn't an official runtime type, it's all custom, and this article doesn't discuss unit testing. I'm not so sure that this is really an issue at all, but I'll defer to @BillWagner.

IEvangelist avatar Aug 04 '22 15:08 IEvangelist

My point is that by not discussing unit testing, anyone doing Test Driven Development and incorporating ideas from this tutorial is in for a potentially nasty surprise, unless they are fully aware of a mocking framework's inability to mock methods with ref struct parameters. I'm guessing not many developers are aware of that. I think the article should carry at minimum a one-sentence warning about this pitfall.

ArticadAlistair avatar Aug 04 '22 15:08 ArticadAlistair