john doe
john doe

Reputation: 9662

Stubbing Code for Test With Linq Expressions and Lambdas

I have query expression in my code that does the following:

repository.Context.AsQueryable<Option>().Where(o => o.Id == id && o.Name == "Something").Select(o => o.Id).ToArray();

How am I going to create a Stub for the above code? It seems like a lot of work. Is there anyway where I can simply ignore what is passed to the Where and Select methods and then return whatever I want to return?

I don't really care about what is passed in the Where and Select method. I just want to return my hard coded list of items in the end.

Upvotes: 3

Views: 182

Answers (2)

kyrylomyr
kyrylomyr

Reputation: 12632

As an option, use your code as a dependency. This way you can stub it without touching context at all. For example:

public class OptionService : IOptionService
{
    private IRepository _repository;

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

    public int[] GetOptionsWithName(int id, string name)
    {
        _repository.Context.AsQueryable<Option>()
                           .Where(o => o.Id == id && o.Name == name)
                           .Select(o => o.Id)
                           .ToArray();
    }
}

public interface IOptionService
{
    int[] GetOptionsWithName(int id, string name);
}

Inject IOptionService into your code with logic similar, how IRepository is injected into the OptionService, and stub the method GetOptionsWithName in the test to return whatever you want.

Upvotes: 2

StriplingWarrior
StriplingWarrior

Reputation: 156624

The short answer is: No, because .Where() and .Select() are extension methods, which cannot be mocked.

The longer answer is: Yes, because .Where() and .Select() on IQueryable<>s do nothing but indicate to the underlying query provider that they were just called. So you could technically create a stub for the query provider and look at what happened to it before it got evaluated.

But the easy answer is: I've found the best approach is to use an actual in-memory representation that is capable of acting like a queryable, like a List. Then, rather than trying to validate the lambda expressions themselves, test the resulting data.

var options = new[] {new Option(...)};
repositoryMock.Setup(r => r.Context).Returns(contextMock.Object);
contextMock.Setup(c => c.AsQueryable<Option>()).Returns(options.AsQueryable());

...

Assert.AreEqual(results[0], options[0].Id);

The downside to this is that there's no way to test that your method only uses expressions that can be translated by your query provider. But I generally find this is "good enough" for unit-testing purposes.

Upvotes: 1

Related Questions