I am able to stub a static extension method using Rhino Mocks but if I cast the return value to another type, I get an error. Why?
using Rhino.Mocks;
public interface INumberGenerator
{
double GetDouble();
}
static class NumberGeneratorExtensionMethods
{
public static double GetTheDouble(this INumberGenerator input)
{
return input.GetDouble();
}
public static decimal GetTheDoubleCastToDecimal(this INumberGenerator input)
{
return (decimal) input.GetDouble();
}
}
class MockExample
{
public void TriggerTheError()
{
var stub = MockRepository.GenerateStub<INumberGenerator>();
// This works
stub.Stub(obj => obj.GetTheDouble()).Return(1.2d);
// This throws the error
stub.Stub(obj => obj.GetTheDoubleCastToDecimal()).Return(1.2m);
}
}
Here is the error:
System.InvalidOperationException : Type 'System.Decimal' doesn't match the return type 'System.Double' for method 'INumberGenerator.GetDouble();'
Warning: this is really a suspicion more than anything else
The problem is that you're not really stubbing the extension methods at all - you're stubbing GetDouble
in both cases.
I haven't looked at the code for Rhino Mocks for a while, but I suspect that the Stub
method is basically saying:
- Get ready for some calls on the mock!
- Call the delegate passed in as an argument
- Note which calls were made
That means you're effectively doing this:
canGetDecimal.Stub(ng => ng.GetDouble()).Return(1.2d);
canGetDecimal.Stub(ng => (decimal) ng.GetDouble()).Return(1.2m);
At that point, it would notice that you called GetDouble
- but then you're trying to set the return value to 1.2m, which is invalid.
You could validate this pretty easily, with some logging. Add a log line to GetTheDoubleCastToDecimal
and then split out the Stub
call from the Return
call:
Console.WriteLine("Before Stub");
var stubCall = canGetDecimal.Stub(obj => obj.GetTheDoubleCastToDecimal();
Console.WriteLine("After Stub");
stubCall.Return(1.2m);
I strongly suspect you'll find that whatever logging you add into the extension method is still logged between "Before Stub" and "After Stub" - showing that the extension method isn't being mocked out.
Moral: don't try to mock/stub extension methods. They're not polymorphic; they're just static methods, and it would be pretty tricky to fake them without deep wizardry. Only try to fake out genuinely polymorphic operations.
There is possibility to stub extension method or any other static method without any framework. This requires a bit of additional afford.
public static class MyExtensions
{
public static Func<int,int, int> _doSumm = (x, y) => x + y;
public static int Summ(this int x, int y)
{
return _doSumm(x, y);
}
}
It allows you to substitute the implementation. You can just change value of _doSumm
field.