Unable to pass null value into mock if original class has constructor with one parameter

880 views
Skip to first unread message

Alexey Diyan

unread,
Jul 20, 2009, 6:50:51 PM7/20/09
to Rhino.Mocks
Hi,

I am using Rhino.Mocks 3.5, assembly version is 3.5.0.1337.
Try to mock class with constructor which has one parameter.
When I pass one null-value into method MockRepository.GenerateMock<T>
(params object[]) I have got following exception:
"MissingMethodException: Can't find a constructor with matching
arguments"

When I cast this null-value to actual type everything works fine.
When I pass two or more null-values (without any type casting)
everything works fine.



You can cut and paste unit tests below:

public class CtorWithOneParam
{
private string _first;

public CtorWithOneParam(string first)
{
_first = first;
}

public virtual string FooMethod()
{
return _first;
}
}

public class CtorWithTwoParams
{
private string _first;
private string _second;

public CtorWithTwoParams(string first, string second)
{
_first = first;
_second = second;
}

public virtual string FooMethod()
{
return _first + _second;
}
}

public class CtorWithThreeParams
{
private string _first;
private string _second;
private string _third;

public CtorWithThreeParams(string first, string second, string
third)
{
_first = first;
_second = second;
_third = third;
}

public virtual string FooMethod()
{
return _first + _second + _third;
}
}

[TestFixture]
public class TestClassMocking
{
[Test]
public void TestCtorWithOneParamWhichEqualsToNull() // Failed
test
{
var result =
MockRepository.GenerateMock<CtorWithOneParam>(null);
Assert.IsNotNull(result);
}

[Test]
public void TestCtorWithOneParamWhichHasKnownTypeEqualsToNull()
{
var result =
MockRepository.GenerateMock<CtorWithOneParam>((string)
null);
Assert.IsNotNull(result);
}

[Test]
public void TestCtorWithOneParamWhichHasKnownType()
{
var result =
MockRepository.GenerateMock<CtorWithOneParam>
(string.Empty);
Assert.IsNotNull(result);
}

[Test]
public void TestCtorWithTwoParams()
{
var result =
MockRepository.GenerateMock<CtorWithTwoParams>(null,
null);
Assert.IsNotNull(result);
}

[Test]
public void TestCtorWithThreeParams()
{
var result =
MockRepository.GenerateMock<CtorWithThreeParams>(null,
null, null);
Assert.IsNotNull(result);
}
}

Ayende Rahien

unread,
Jul 21, 2009, 1:54:51 PM7/21/09
to Rhino...@googlegroups.com
That is because the way C# behaves in this situation, you need to use

new[]{null} instead 

Kenneth Xu

unread,
Jul 21, 2009, 4:23:20 PM7/21/09
to Rhino...@googlegroups.com
I have my own class to workaround those issues, here is the part to
workaround the problem you mentioned:

public static class Mockery
{
private static readonly object [] SingleNull = new object[1];

public static T GenerateMock<T>(params object[] argumentsForConstructor)
where T : class
{
return
MockRepository.GenerateMock<T>(argumentsForConstructor ?? SingleNull);
}

/* other stuff */
}

Drop the class in your project and then replace MockRepository with
Mockery in your tests. It should pass.

Krzysztof Koźmic (2)

unread,
Jul 22, 2009, 3:01:34 AM7/22/09
to Rhino.Mocks
This gets messy.
What if you have a constructor: .ctor(object[] obj)

say you have to pass null as obj.
With you approach you'll pass new object[1] instead.

That's just drawback of using params object[] because this means:
"literally anything". That's why we deprecated this in DynamicProxy
2.2.

On Jul 21, 10:23 pm, Kenneth Xu <kenne...@gmail.com> wrote:
> I have my own class to workaround those issues, here is the part to
> workaround the problem you mentioned:
>
>     public static class Mockery
>     {
>         private static readonly object [] SingleNull = new object[1];
>
>         public static T GenerateMock<T>(params object[] argumentsForConstructor)
>             where T : class
>         {
>             return
> MockRepository.GenerateMock<T>(argumentsForConstructor ?? SingleNull);
>         }
>
>         /* other stuff */
>     }
>
> Drop the class in your project and then replace MockRepository with
> Mockery in your tests. It should pass.
>

Kenneth Xu

unread,
Jul 22, 2009, 8:10:46 AM7/22/09
to Rhino...@googlegroups.com
No it's not. With the constructor you defined, you should get a null, not object[1]. I'm away from my PC so cannot test it. But it is the first element in the object[] that supposed to be passed to the constructor. If not, then, IMHO, it is another issue to be resolved.

Sent from my Verizon Wireless BlackBerry

-----Original Message-----
From: Krzysztof Ko mic (2) krzy...@kozmic.pl

Date: Wed, 22 Jul 2009 00:01:34
To: Rhino.Mocks<Rhino...@googlegroups.com>
Subject: [RhinoMocks] Re: Unable to pass null value into mock if original
class has constructor with one parameter

Kenneth Xu

unread,
Jul 22, 2009, 10:38:40 AM7/22/09
to Rhino...@googlegroups.com
Here is the test case to prove:

public class Foo
{
public Foo(object [] o)
{
Console.WriteLine(o??(object)"null array");
}

public static void Main(string[] args)
{
Mockery.GenerateMock<Foo>(null);
}
}

output: null array

2009/7/22 Krzysztof Koźmic (2) <krzy...@kozmic.pl>:


> That's just drawback of using params object[] because this means:
> "literally anything". That's why we deprecated this in DynamicProxy

Krzysztof,

I'm curious to know what is the replacement for this.

Cheers,
Kenneth

Krzysztof Koźmic (2)

unread,
Jul 23, 2009, 5:43:24 AM7/23/09
to Rhino.Mocks
If you do it at this point it will. Let's try another one.

Say you have a ctor like this: Foo.ctor(IBar[] bars); and you want to
pass empty array of IBar

if you call Mockery.GenerateMock<Foo>(new IBar[]{}); it won't work.
because arrays in .NET are covariant and CLR will treat this as if you
passed empty array of objects as your params array, which would
indicate you want to call the default constructor, which is not what
you want in this case. You'd have to pass it explicitly as new object[]
{new IBar[]{}} which is inconsistent with the other cases and
confusing.

On Jul 22, 4:38 pm, Kenneth Xu <kenne...@gmail.com> wrote:
> Here is the test case to prove:
>
>     public class Foo
>     {
>         public Foo(object [] o)
>         {
>             Console.WriteLine(o??(object)"null array");
>         }
>
>         public static void Main(string[] args)
>         {
>             Mockery.GenerateMock<Foo>(null);
>         }
>     }
>
> output: null array
>
> 2009/7/22 Krzysztof Koźmic (2) <krzysz...@kozmic.pl>:

Kenneth Xu

unread,
Jul 23, 2009, 1:09:08 PM7/23/09
to Rhino...@googlegroups.com
2009/7/23 Krzysztof Koźmic (2) <krzy...@kozmic.pl>:

> If you do it at this point it will. Let's try another one.

Just to clarify that my workaround is just for that specific use case.
There is no reason to pass in a single null to GenerateMock other then
pass it down to the constructor. It is not intended for a general
solution to params object[].

> Say you have a ctor like this: Foo.ctor(IBar[] bars); and you want to
> pass empty array of IBar

Well, you can still workaround it in the GenerateMock use case. I
think it has no meaning of passing an IBar array other then giving it
to the constructor.

public static T GenerateMock<T>(params object[] argumentsForConstructor)
where T : class
{

if (argumentsForConstructor == null)
{
argumentsForConstructor = SingleNull;
}
else if
(argumentsForConstructor.GetType().GetElementType() != typeof(object))
{
argumentsForConstructor = new object[]{argumentsForConstructor};
}
return MockRepository.GenerateMock<T>(argumentsForConstructor);
}

> if you call Mockery.GenerateMock<Foo>(new IBar[]{}); it won't work.
> because arrays in .NET are covariant and CLR will treat this as if you
> passed empty array of objects as your params array, which would
> indicate you want to call the default constructor, which is not what
> you want in this case. You'd have to pass it explicitly as new object[]
> {new IBar[]{}} which is inconsistent with the other cases and
> confusing.

This made me recall the longer discussion on this topic when Java 1.5
was released. Yes, I agree there are confusions by the design of using
object array for variable parameters. But I believe, IMHO, those
workaround helps to reduce the confusions brought by variable
parameter and gives less surprise to API users.

If you don't mind, I'm still curious about the alternative in DP 2.2 :)

Thanks,
Kenneth

Reply all
Reply to author
Forward
0 new messages