我正在尝试测试我编写的一些代码,这些代码试图使用Machine.Fakes(在引擎盖下使用Moq)来模拟一个函数。有关示例,请参阅下面的代码。
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);
}
}
测试就在这里
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;
}
我得到了上面不支持的表达式,所以我将IMessageRouters
上的where方法更改为以下内容:
public IEnumerable<IMessageRouter> Where(Expression<Func<IMessageRouter, bool>> func)
{
return _routers.Where(func.Compile());
}
现在我收到此错误
对象实例不是由Moq创建的 参数名称:mocked
有什么想法吗?
修改
所以我尝试编写另一个没有machine.fakes的测试,按照Mocking methods with Expression<Func<T,bool>> parameter using Moq。事实证明这是一个明显的问题。实际RoutingEngine中使用的func没有被模拟
The<IMessageRouters>()
.WhenToldTo(x => x.Where(router => router.CanRoute(Param.IsAny<ProcessSkuCostUpdated>())))
.Return(new List<IMessageRouter> {Router});
上面与在运行时执行的位置没有关系,并且不能在编译时将func编译为私有方法。似乎是在模拟func,我需要将其推送到界面。虽然闻起来因为我推动内部行为纯粹是为了测试。
答案 0 :(得分:1)
我看到了一种避免模仿Func<>
的方法。我希望你有意思:)为什么不忘记广义Where(Func<>)
方法并提供具体的查询方法。您拥有入站消息和路由器。
public class MessageRouters : IMessageRouters
{
public IList<IMessageRouter> _routers = new List<IMessageRouter>();
public IEnumerable<IMessageRouter> For<T>(T inbound)
{
return _routers.Where(x => x.CanRoute(inbound));
}
public void Add(IMessageRouter messageRouter)
{
_routers.Add(messageRouter);
}
}
受测试的课程变得更简单(签名,没有Func<>
)。
public class RoutingEngine : IRoutingEngine
{
private readonly IMessageRouters _messageRouters;
public RoutingEngine(IMessageRouters messageRouters)
{
_messageRouters = messageRouters;
}
public void Route<T>(T inbound)
{
var messageRouters = _messageRouters.For(inbound);
foreach(var router in messageRouters)
router.Route(inbound);
}
}
我猜你也不需要检查入站消息的实际实例,也许你可以通过Type
检查来解决,比如
public IEnumerable<IMessageRouter> For<T>() { ... }
和
var messageRouters = _messageRouters.For<T>();
你现在不必模拟任何Func<>
,你可以做一个断言被调用(或者看看Moq)。
答案 1 :(得分:1)
我发现您的测试代码有两个问题:
Where()
设置IMessageRouters
电话时使用的表达式过于明确。它不应该关心通过什么确切的函数。Route()
是否已调用Subject
。相反,您应该验证Message
是否已传递给IMessageRouter
。作为额外的改进,您可以省略Router
字段并直接使用The<IMessageRouter>()
。
[Subject(typeof(RoutingEngine))]
public class when_routing_a_message_with_fakes : WithSubject<RoutingEngine>
{
Establish that = () =>
{
Message = new MyMessage { SomeValue = 1, SomeOtherValue = 11010 };
The<IMessageRouter>().WhenToldTo(x => x.CanRoute(Message)).Return(true);
The<IMessageRouters>().WhenToldTo(x => x.Where(Param<Func<IMessageRouter, bool>>.IsAnything))
.Return(new List<IMessageRouter> { The<IMessageRouter>() });
};
Because of = () => Subject.Route(Message);
It should_route_the_message = () =>
The<IMessageRouter>().WasToldTo(x => x.Route(Message));
static MyMessage Message;
}