views:

832

answers:

3

In the .net 3.5 project that I am working on right now, I was writing some tests for a service class.

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
}

and I was having a hard time figuring out how to stub or mock the

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

part.

Here's what I got so far:

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

}

[Note: class names have been changed to protect the innocent]

But this fails with a System.NullReferenceException at

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

So my question is:

How do I mock or stub the IQueryable.Where function with RhinoMocks and get this test to pass?

If my current setup won't allow me to mock or stub IQueryable, then give a reasoned explanation why.

Thanks for reading this epically long question.

+2  A: 

Without using Rhino mocks, you can create a List and then call .AsQueryable() on it. e.g.

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

var actionTypeRepo = actionTypeList.AsQueryable();

That will at least get you a fake repository, but it won't let you verify that methods were called.

Lance Fisher
+3  A: 

Where is an extension method, this is not a method implemented by the interface IQueriable. Look at the members of IQueriable: http://msdn.microsoft.com/en-us/library/system.linq.iqueryable_members.aspx

An extension method is static and can't be mocked. IMO, there is no need to mock Where, because it's part of the language. You should only mock the repository.

Edit, Example:

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

}

Note: you don't need to expect the call, because your method depends on the return value of the mock. If it wouldn't call it, it would fail on the asserts. This makes your test easier to maintain.

Stefan Steinegger
+!, nice answer.
Mark Rogers
+4  A: 

I initially wanted to mock out calls like 'IQueryable.Where(Func)' too but I think it is testing at the wrong level. Instead in my tests I just mocked out IQueryable and then check the result:

// 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 Quinane
I was about to say the exact same thing with a very similar example. I will not commit. This is very correct.As Luke is showing, you do not need to mock out Where(). That is part of your logic. You are testing that the lambda passed into Where() works properly. Just make sure your actionList.Query returns data that can be filtered with Where() and validate that the query worked properly based on the set of data returned.Good job, Luke.
Brian Genisio
+!, nice answer
Mark Rogers