Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moq cannot mock a method that contains a default value of null w/out explicitly setting that value in the mock's expression tree #316

Closed
mgmccarthy opened this issue Jan 6, 2017 · 4 comments

Comments

@mgmccarthy
Copy link

This is related to a MediatR 3.0 upgrade, but I'm sure the problem extends to any method that is mock-able that uses a default value as one of its parameters.

Here is the interface I'm trying to mock:

public interface IMediator
{
    Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = null);

    Task Send(IRequest request, CancellationToken cancellationToken = null);

    Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = null) where TNotification : INotification;
}

We have thousands of unit tests that mocked the older version of this interface (w/out the CancellationToken). An example of that code:

mockMediator.Verify(mock => mock.Send(It.Is<CampaignDetailQuery>(c => c.CampaignId == campaignId)));

after upgrading the library to the interface above, for each unit test I'm getting this error:

An expression tree may not contain a call or invocation that uses optional arguments

We'd have to change thousands of unit tests to explicitly pass the default parameter to the expression tree like this:

mockMediator.Verify(mock => mock.Send(It.Is<IndexQuery>(q => q.OrganizationId == null), default(CancellationToken)));

which is a lot of changes.

Any plans for Moq to support NOT having to explicitly pass in default parameters in expression trees to mockable members?

@kzu
Copy link
Contributor

kzu commented Jan 8, 2017 via email

@stakx
Copy link
Contributor

stakx commented Jun 5, 2017

As long as Moq is based on expression trees, and that compiler restriction isn't relaxed, there's just no way to solve this.

Here's a suggestion for a workaround that may or may not work in your situation.

  1. Put the CancellationToken-less methods back in the interface. (This will solve the immediate issue encountered in your Moq-based tests.) For example:

    partial class IMediator
    {
        Task Send(IRequest request);
        Task Send(IRequest request, CancellationToken cancellationToken);
    }
  2. Then declare a base type MediatorBase from which your implementations will inherit (instead of implementing IMediator directly):

    abstract partial class MediatorBase : IMediator
    {
        public Task Send(IRequest request) => Send(request, CancellationToken.None);
        public abstract Task Send(IRequest request, CancellationToken cancellationToken);
    }

    Or, if you prefer explicit interface implementation:

    abstract partial class MediatorBase : IMediator
    {
        Task IMediator.Send(IRequest request) => Send(request, CancellationToken.None);
        Task IMediator.Send(IRequest request, CancellationToken cancellationToken) => Send(request, cancellationToken);
        protected abstract Task Send(IRequest request, CancellationToken cancellationToken);
    }

(P.S.: Starting with C# 8 / .NET Core 3, you may be able to skip the base class and instead define the CancellationToken-less method as a default interface method.)

@stakx stakx closed this as completed Jun 5, 2017
@DaanDL
Copy link

DaanDL commented Jul 25, 2017

I got this working by doing the following:

mediatorMock.Setup(x => x.Send(It.IsAny<SomeCommand>(), It.IsAny<CancellationToken>())) .ReturnsAsync(new SomeResult());

and then

mediatorMock.Verify(x => x.Send(It.IsAny<SomeCommand>(), It.IsAny<CancellationToken>()));

@JoeGC1969
Copy link

Thanks DaanDL, this worked for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants