I'm trying to test some code I've written have run in to issues trying to mock a func using Machine.Fakes (which uses Moq under the hood). See the code below for an example.
public class RoutingEngine : IRoutingEngine
{
private readonly IMessageRouters _messageRouters;
public RoutingEngine(IMessageRouters messageRouters)
{
_messageRouters = messageRouters;
}
public void Route<T>(T inbound)
{
var messageRouters = _messageRouters.Where(x => x.CanRoute(inbound));
foreach(var router in messageRouters)
router.Route(inbound);
}
}
public class MessageRouters : IMessageRouters
{
public IList<IMessageRouter> _routers = new List<IMessageRouter>();
public IEnumerable<IMessageRouter> Where(Func<IMessageRouter, bool> func)
{
return _routers.Where(func);
}
public void Add(IMessageRouter messageRouter)
{
_routers.Add(messageRouter);
}
}
And the test is here
public class when_routing_a_message_with_fakes : WithSubject<RoutingEngine>
{
Establish that = () =>
{
Message = new MyMessage{ SomeValue= 1, SomeOtherValue = 11010 };
Router = The<IMessageRouter>();
Router.WhenToldTo(x => x.CanRoute(Message)).Return(true);
The<IMessageRouters>().WhenToldTo(x => x.Where(router => router.CanRoute(Message))).Return(new List<IMessageRouter> { Router });
};
Because of = () => Subject.Route(Message);
It should_do_route_the_message = () => Subject.WasToldTo(x => x.Route(Param.IsAny<MyMessage>()));
static MyMessage Message;
static IMessageRouter Router;
}
I get an unsupported expression for the above so I changed the where method on the IMessageRouters to the following:
public IEnumerable<IMessageRouter> Where(Expression<Func<IMessageRouter, bool>> func)
{
return _routers.Where(func.Compile());
}
Now I get this error
Object instance was not created by Moq.
Parameter name: mocked
Any ideas?
EDIT
So I tried writing another test without machine.fakes, as per Mocking methods with Expression<Func<T,bool>> parameter using Moq. Turns out it's an obvious problem. The func used in the real RoutingEngine is not being mocked
The<IMessageRouters>()
.WhenToldTo(x => x.Where(router => router.CanRoute(Param.IsAny<ProcessSkuCostUpdated>())))
.Return(new List<IMessageRouter> {Router});
The above has no bearing on the Where being executed at runtime and can't be as the func is compiled down to a private method at compile time. Seems like to mock the func, I need to push it up to an interface. Smells though as I'm pushing up internal behavior purely for testing.
I see two issues with your test code:
Where()call onIMessageRoutersis too explicit. It should not care about what exact function is passed.Route()has been called on theSubject. Instead you should verify whether theMessagehas been passed to theIMessageRouter.As an additional improvement, you can omit the
Routerfield and useThe<IMessageRouter>()directly.