We may have to try this later. We are on Isolator 5.1.0, which doesn't have the duck typing, and we have yet to schedule the upgrade for 5.1.1.
The thing we're trying to mock is a generic base class with a protected static method, like:
public abstract FooBase<T>
{
protected static void Execute(Action<T> action)
{
// some initialization code happens here that we want
// to mock, like this:
T obj = new T();
obj.DoSomeInitialization();
obj.SomeProperty = GetSomeValueFromConfig();
// and we just want to skip all of that and run
// the action because that's the part we want to
// test... and it's an anonymous delegate with a
// closure, so we can't just pull it out and test it
// separately....
action(T);
}
}
public class DerivedClass : FooBase<ObjType>
{
// here's the method we actually want to test:
public Guid DoSomething()
{
// stuff happens in here that looks like:
Guid returnValue;
Execute(delegate(ObjType o)
{
// work happens and somewhere in here
// we see something like:
returnValue = o.SomeMethod();
}
return returnValue;
}
}
So, as you can see, we have a bit of a tricky situation. The static belongs to a closed generic base type even though it's declared on an open generic base type, it's nonpublic, and the action we're performing is a closure. The initial desire was to set up a DynamicReturnValue like:
ObjType obj = RecorderManager.CreateMockedObject<ObjType>();
DynamicReturnValue executeReturnValue = new DynamicReturnValue((parameters, context) =>
{
var action = parameters[0] as Action<ObjType>;
Assert.IsNotNull(action);
action(obj);
return null;
});
MockObject<FooBase<ObjType>> mockBase = MockManager.MockObject<FooBase<ObjType>>();
mockBase.CallStatic.ExpectAndReturn("Execute", executeReturnValue);
That way we'd have a reference to the object being passed in, we could skip the initialization calls in the Execute method, but still execute the anonymous delegate and verify functionality there. Unfortunately, since (in this example) ObjType has to have some methods mocked on it inside the anonymous delegate, we need to have mocks running, so this doesn't work - when we hit the DynamicReturnValue, mocks "shut down" and it actually tries to run the methods on ObjType.
Anyway, yet another case where this would be handy. I'm not sure how we'd do this with the duck typing solution - can you swap in an object with a matching protected static method and have that work?
BTW, if you try to use mocks inside a DynamicReturnValue, you see some interesting debugger interaction if you're running the tests through the debugger. A method will be outlined as though it's being mocked, but you'll actually be stepping through the code because in reality it's not being mocked. Causes for a little confusion, but no showstopper.