nsubstitute / NSubstitute.Analyzers

Roslyn analysers for NSubstitute.
MIT License
30 stars 7 forks source link

NS5000 warning when using custom substitute extensions for checking stuff #217

Closed rklec closed 4 months ago

rklec commented 6 months ago

Background

A common use case might be a substitute extension in such a way:

    /// <summary>
    /// Checks this substitute has not received the call with the given log level.
    /// </summary>
    /// <remarks>See <see cref="SubstituteExtensions.DidNotReceiveWithAnyArgs{T}"/></remarks>
    /// <param name="logger">the substitute to check</param>
    /// <param name="logLevel">the log level which should not have received anything</param>
    /// <exception cref="ArgumentOutOfRangeException">if the log level is invalid</exception>
    public static void DidNotReceiveWithAnyArgsFor(this ILogger logger, LogLevel logLevel)
    {
        switch (logLevel)
        {
            case LogLevel.Debug:
                logger.DidNotReceiveWithAnyArgs().Debug(default!);
                logger.DidNotReceiveWithAnyArgs().Debug(default!, default!);
                break;
            case LogLevel.Info:
                logger.DidNotReceiveWithAnyArgs().Info(default!);
                logger.DidNotReceiveWithAnyArgs().Info(default!, default!);
                break;
            case LogLevel.Warn:
                logger.DidNotReceiveWithAnyArgs().Warn(default!);
                logger.DidNotReceiveWithAnyArgs().Warn(default!, default!);
                break;
            case LogLevel.Error:
                logger.DidNotReceiveWithAnyArgs().Error(default!);
                logger.DidNotReceiveWithAnyArgs().Error(default!, default!);
                break;
            case LogLevel.Fatal:
                logger.DidNotReceiveWithAnyArgs().Fatal(default!);
                logger.DidNotReceiveWithAnyArgs().Fatal(default!, default!);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, "Invalid log level");
        }
    }

The reason for coding this may be, that the interface could take different "overloads" or better, different method variations, that you logically wanted to check for multiple methods at once (aka/i.e. https://github.com/nsubstitute/NSubstitute/issues/660). So it may be reasonable to write a wrapper/extension for that. See this interface for reference:

  public interface ILogger
  {
    void Debug(string message);

    void Debug(string message, Exception ex);

    void Error(string message);

    void Error(string message, Exception ex);

    void Fatal(string message);

    void Fatal(string message, Exception ex);

    void Info(string message);

    void Info(string message, Exception ex);

    void Warn(string message);

    void Warn(string message, Exception ex);
  }

Now, this works without any issues.

Issue

However if you want to use the actual extension, you get an annoying Analyzer warning, which you cannot fix:

logger.DidNotReceiveWithAnyArgsFor(LogLevel.Fatal);

NS5000: Unused received check. To fix, make sure there is a call after "DidNotReceiveWithAnyArgs". Correct: "sub.DidNotReceiveWithAnyArgs().SomeCall();". Incorrect: "sub.DidNotReceiveWithAnyArgs0;"

https://github.com/nsubstitute/NSubstitute.Analyzers/blob/dev/documentation/rules/NS5000.md

Edit: Oh, weird, while writing this issue, it suddenly disappeared, hmm... strange?

Versions

net6.0 FluentAssertions 6.11.0 FluentAssertions.Analyzers 0.24.0

tpodolak commented 4 months ago

Can't reproduce. Looks like IDE glitch as analyzers check if Received like methods are defined in NSubstitute assembly