【问题标题】:How do I mock a Func<> using Machine.Fakes (Moq)?如何使用 Machine.Fakes (Moq) 模拟 Func<>?
【发布时间】:2013-10-31 13:33:19
【问题描述】:

我正在尝试测试我编写的一些代码在尝试使用 Machine.Fakes(它在引擎盖下使用 Moq)模拟 func 时遇到问题。有关示例,请参见下面的代码。

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 创建的。
参数名称:模拟

有什么想法吗?

编辑

所以我尝试按照Mocking methods with Expression<Func<T,bool>> parameter using Moq 编写另一个没有 machine.fakes 的测试。事实证明这是一个明显的问题。真正的 RoutingEngine 中使用的 func 没有被模拟

The<IMessageRouters>()
    .WhenToldTo(x => x.Where(router => router.CanRoute(Param.IsAny<ProcessSkuCostUpdated>())))
    .Return(new List<IMessageRouter> {Router});

以上内容与运行时执行的 Where 无关,也不能因为 func 在编译时被编译为私有方法。似乎要模拟函数,我需要将其推送到界面。闻起来,因为我纯粹为了测试而推动内部行为。

【问题讨论】:

    标签: c# moq mspec machine.fakes


    【解决方案1】:

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

    1. 用于设置Where() 调用IMessageRouters 的表达式过于明确。它不应该关心传递了什么确切的函数。
    2. 您正在验证Route() 是否已在Subject 上调用。相反,您应该验证 Message 是否已传递给 IMessageRouter

    作为额外的改进,您可以省略Router 字段,直接使用The&lt;IMessageRouter&gt;()

    [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;
    }
    

    【讨论】:

      【解决方案2】:

      我看到了一种完全避免嘲笑Func&lt;&gt; 的方法。我希望它对你感兴趣:) 为什么不忘记广义的Where(Func&lt;&gt;) 方法并提供具体的查询方法。您拥有入站消息和路由器。

      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&lt;&gt;)。

      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&lt;&gt;s,您只需执行一个 assert-was-call (或者在 Moq 中看起来如何)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-18
        • 2010-10-19
        • 2016-09-30
        • 2019-10-06
        • 1970-01-01
        • 2020-10-27
        相关资源
        最近更新 更多