devlooped / moq

The most popular and friendly mocking framework for .NET
Other
5.94k stars 802 forks source link

Allow Setup on extension methods #1106

Closed TooJooGoo closed 3 years ago

TooJooGoo commented 3 years ago

Hi,

I'am working for a major company. My job is to port our mocking-framework, currently written in RhinoMocks, over to Moq.

With RhinoMocks we are able to Stub/Expect on extension methods. With Moq unfortunately the Setup on extension methods is unsupported, throwing an "System.NotSupportedException" expection.

At the moment this deficit is a show-stopper for us, because we have tons of DLLs which make extensive use of extension methods. There is not chance for us to change them!

Please See my code down below:

// Just a pseude code of our test
namespace Sample
{
    public interface InterfaceB
    {
        string P1 { get; set; }
    }

    public static class InterfaceBExtensions
    {
        public static object FuncObjObj(this InterfaceB b, object arg0)
        {
            return arg0;
        }
    }
}

// Here the test itself:
var proxy = new Mock<InterfaceB>();
proxy.Setup(x => x.FuncObjObj("1")).Returns("1");

In the last line we get the expection: System.NotSupportedException expection: 'Unsupported expression: x => x.FuncObjObj("1") Hint: This happens for any MockBehavior Loose or Strict.

Will it be possible to support the "Setup on extension methods" in Moq? We would really appreciate if this would be possible!!

stakx commented 3 years ago

Will it be possible to support the "Setup on extension methods" in Moq?

No. When Moq creates a mock object, it does so by creating a subclass / implementing class of the mocked type, then it overrides all overridable methods to forward calls to setups. Since extension methods are static methods sitting in static classes, it's impossible to override them in any way.

From what I remember, Rhino Mocks sort-of supports this scenario in the sense that extension methods often call virtual methods on the extended type which, in turn, can be mocked. So Rhino Mocks doesn't really intercept the extension method; it can only intercept methods called by extension methods. (IIRC, older versions of Moq < 4.7 might have had the same ability.)

We could do the same thing in Moq (again), however I strongly believe that this would be a very fragile feature that works only part of the time. We would give the impression that extension methods are supported when in fact they are not. And then we'd have to deal with people sending reports because they don't understand why Moq allows setting up extension methods, yet the setups don't trigger reliably. Not a good idea.

IMHO it's much better for Moq to tell you as soon as possible that something isn't going to work as expected, instead of failing silently / simply not doing what you expected it to.

If you need to mock/stub non-overridable type members (fields, sealed/static/private methods, etc.) you will need to look elsewhere, e.g. Microsoft Fakes or Telerik JustMock. These use a different interception mechanism (the CLR's unmanaged profiling API, IIRC).