我如何模拟Func<>使用Machine.Fakes(Moq)?

时间:2013-10-31 13:33:20

标签: c# moq mspec machine.fakes

我正在尝试测试我编写的一些代码,这些代码试图使用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,我需要将其推送到界面。虽然闻起来因为我推动内部行为纯粹是为了测试。

2 个答案:

答案 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)

我发现您的测试代码有两个问题:

  1. 您在Where()设置IMessageRouters电话时使用的表达式过于明确。它不应该关心通过什么确切的函数。
  2. 您正在验证Route()是否已调用Subject。相反,您应该验证Message是否已传递给IMessageRouter
  3. 作为额外的改进,您可以省略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;
    }