Node.JS
Node.JS

Reputation: 1578

Found a potential bug in Moq library while selecting a constructor

I have the following setup and using Moq version 4.13.1. I am not sure this is a bug or not but I am wondering how can I get around this problem and pass null to the constructor argument.

public class Foo
{
    public Foo() { Console.Write("Foo() called"); }

    public Foo(string name, A _, Bar bar): this() { Console.Write("Foo(A) called"); }

    public Foo(string name, B _, Bar bar): this() { Console.Write("Foo(B) called"); }
}

public class A { }

public class B { }

public class Bar { }

class Program
{
    static void Main(string[] args)
    {
        // using default(A) will yield the same error
        var fooMock = new Mock<Foo>("Hello world!", (A) null, new Bar());

        var instance = fooMock.Object;

        Console.WriteLine(instance);
    }
}

I am getting the following error:

Unhandled exception. System.Reflection.AmbiguousMatchException: Ambiguous match found.

Stacktrace:

   at System.DefaultBinder.BindToMethod(BindingFlags bindingAttr, MethodBase[] match, Object[]& args, ParameterModifier[] modifiers, CultureInfo cultureInfo, String[] names, Object& state)
   at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture)
   at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance(Type proxyType, List`1 proxyArguments, Type classToProxy, Object[] constructorArguments)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
   at Moq.CastleProxyFactory.CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, Object[] arguments)
   at Moq.Mock`1.InitializeInstance()
   at Moq.Mock`1.OnGetObject()
   at Moq.Mock.get_Object()
   at Moq.Mock`1.get_Object()

Upvotes: 0

Views: 355

Answers (1)

canton7
canton7

Reputation: 42320

Your (A)null casts won't work, because that constructor overload of Mock<T> takes a params object[], so all of your parameters end up as objects anyway. An ambiguous match in this case is expected.

However, there is a constructor overload which takes an Expression<Func<Foo>>:

var fooMock = new Mock<Foo>(() => new Foo("Hello, World", (A)null, new Bar()));

This lets you unambiguously choose the constructor to call. However, this also fails with the same error, and I would consider that a bug, or a least a missed opportunity. It would probably be a good idea to raise an issue: this feature was introduced in #888.

You can do a slightly hacky workaround:

public class MockFoo : Foo
{
    public MockFoo(string name, A _, Bar bar) : base(name, _, bar) { }
}

var fooMock = new Mock<MockFoo>("Hello, World", (A)null, new Bar());

Now there's only one constructor (because constructors aren't inherited in C#), and there's no longer an ambiguous match.

Upvotes: 3

Related Questions