【问题标题】:How do you stub IQueryable<T>.Where(Func<T, bool>) with Rhino Mocks?你如何使用 Rhino Mocks 存根 IQueryable<T>.Where(Func<T, bool>) ?
【发布时间】:2010-10-21 22:23:33
【问题描述】:

在我现在正在处理的 .net 3.5 项目中,我正在为服务类编写一些测试。

public class ServiceClass : IServiceClass
{
     private readonly IRepository _repository;

     public ServiceClass(IRepository repository)
     {
          _repository = repository;
     }

     #region IServiceClass Members

     public IEnumerable<ActionType> GetAvailableActions()
     {
         IQueryable<ActionType> actionTypeQuery = _repository.Query<ActionType>();
         return actionTypeQuery.Where(x => x.Name == "debug").AsEnumerable();
     }

     #endregion
}

我很难弄清楚如何存根或模拟

actionTypeQuery.Where(x => x.Name == "debug")

部分。

这是我目前得到的:

[TestFixture]
public class ServiceClassTester
{
    private ServiceClass _service;
    private IRepository _repository;
    private IQueryable<ActionType> _actionQuery;
    [SetUp]
    public void SetUp()
    {
        _repository = MockRepository.GenerateMock<IRepository>();
        _service = new ServiceClass(_repository);
    }

    [Test]
    public void heres_a_test()
    {
        _actionQuery = MockRepository.GenerateStub<IQueryable<ActionType>>();

        _repository.Expect(x => x.Query<ActionType>()).Return(_actionQuery);
        _actionQuery.Expect(x => x.Where(y => y.Name == "debug")).Return(_actionQuery);

        _service.GetAvailableActions();

        _repository.VerifyAllExpectations();
        _actionQuery.VerifyAllExpectations();
    }

}

[注意:类名已更改以保护无辜]

但这失败了,System.NullReferenceException

_actionQuery.Expect(x => x.Where(y => y.Name == "debug")).Return(_actionQuery);

所以我的问题是:

如何使用 RhinoMocks 模拟或存根 IQueryable.Where 函数并让此测试通过?

如果我当前的设置不允许我模拟或存根 IQueryable,请给出合理的解释。

感谢您阅读这个冗长的问题。

【问题讨论】:

  • 前两个答案的质量都足够高,可以选择。谢谢,对不起,如果我没有选择你的。

标签: c# unit-testing nunit rhino-mocks iqueryable


【解决方案1】:

我遇到过类似的问题,我试图将谓词表达式存根到包含字符串的 Find-method,但 String 是引用类型,当然是不可变的。在我创建 testPredicate 之前没有成功,我将它传递给存根、实际 SUT 并最终断言。下面的代码有效。

    [Test()]
    public void Search_Apartment_With_Spesific_Address()
    {
        //ARRANGE
        var repositoryMock = MockRepository.GenerateMock<IApartmentRepository>();
        var notificationMock = MockRepository.GenerateMock<INotificationService>();
        var service = new ApartmentService(repositoryMock, notificationMock);
        var apartment = new List<Apartment> {new Apartment {Address = "Elm Street 2"}}.AsQueryable();

        Expression<Func<Apartment, bool>> testPredicate = a => a.Address == "Elm Street 2";
        repositoryMock.Stub(x => x.Find(testPredicate)).Return(apartment);

        //ACT
        service.Find(testPredicate);

        //ASSERT
        repositoryMock.AssertWasCalled(x => x.Find(testPredicate));
    }

【讨论】:

    【解决方案2】:

    Where是一个扩展方法,这不是IQueriable接口实现的方法。看看IQueriable的成员:http://msdn.microsoft.com/en-us/library/system.linq.iqueryable_members.aspx

    扩展方法是静态的,不能被模拟。 IMO,没有必要模拟Where,因为它是语言的一部分。您应该只模拟存储库。

    编辑,示例:

    [TestFixture]
    public class ServiceClassTester
    {
        private ServiceClass _service;
        private IRepository _repository;
        private IQueryable<ActionType> _actionQuery;
    
        [SetUp]
        public void SetUp()
        {
            _service = new ServiceClass(_repository);
    
            // set up the actions. There is probably a more elegant way than this.
            _actionQuery = (new List<ActionType>() { ActionA, ActionB }).AsQueryable();
    
            // setup the repository
            _repository = MockRepository.GenerateMock<IRepository>();
            _repository.Stub(x => x.Query<ActionType>()).Return(_actionQuery);
        }
    
        [Test]
        public void heres_a_test()
        {
            // act
            var actions = _service.GetAvailableActions();
    
            // assert
            Assert.AreEqual(1, actions.Count());
            // more asserts on he result of the tested method
        }
    
    }
    

    注意:你不需要期待调用,因为你的方法依赖于模拟的返回值。如果它不调用它,它将在断言上失败。这使您的测试更易于维护。

    【讨论】:

      【解决方案3】:

      我最初也想模拟像“IQueryable.Where(Func)”这样的调用,但我认为它是在错误的级别进行测试。相反,在我的测试中,我只是模拟了 IQueryable,然后检查结果:

      // Setup a dummy list that will be filtered, queried, etc
      var actionList = new List<ActionType>()
      {
          new ActionType() { Name = "debug" },
          new ActionType() { Name = "other" }
      };
      _repository.Expect(x => x.Query<ActionType>()).Return(actionList);
      
      var result = _service.GetAvailableActions().ToList();
      
      // Check the logic of GetAvailableActions returns the correct subset 
      // of actionList, etc:
      Assert.That(result.Length, Is.EqualTo(1));
      Assert.That(result[0], Is.EqualTo(actionList[0]);
      
      _repository.VerifyAllExpectations();
      

      【讨论】:

      • 我正准备用一个非常相似的例子说同样的话。我不会承诺。这是非常正确的。正如 Luke 所展示的,您不需要模拟 Where()。这是你逻辑的一部分。您正在测试传递给 Where() 的 lambda 是否正常工作。只需确保您的 actionList.Query 返回可以使用 Where() 过滤的数据,并根据返回的数据集验证查询是否正常工作。干得好,卢克。
      【解决方案4】:

      不使用 Rhino 模拟,您可以创建一个 List,然后在其上调用 .AsQueryable()。例如

      var actionTypeList = new List<ActionType>() {
          new ActionType {},  //put your fake data here
          new ActionType {}
      };
      
      var actionTypeRepo = actionTypeList.AsQueryable();
      

      这至少会给你一个虚假的存储库,但它不会让你验证方法是否被调用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-15
        相关资源
        最近更新 更多