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

Created on 6 Jan 2017  Â·  4Comments  Â·  Source: moq/moq4

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?

Most helpful comment

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>()));

All 4 comments

The error seems to be a compilation error, so, Moq can't change the
compiler, no

On Fri, Jan 6, 2017, 8:35 AM Michael McCarthy notifications@github.com
wrote:

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 Send(IRequest 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(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(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?

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/moq/moq4/issues/316, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAKW625YhHcHxIRtBG5_Pc2vAYuHvzSDks5rPkMvgaJpZM4Lct9q
.

>

--
/kzu from mobile

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.)

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>()));

Thanks DaanDL, this worked for me.

Was this page helpful?
0 / 5 - 0 ratings