Reputation: 3164
While writing a unit test for a Sitecore 7 search class, following the pattern established here (https://github.com/Sitecore/sitecore-seven-unittest-example/blob/master/ExampleFixture.cs), I am running into an issue where in order to make my tests pass I need to add AsEnumerable()
to the Sitecore LINQ expression.
Here is my unit test:
[Test]
public void Provider_WhenCalled_MatchesPath()
{
//arrange
var index = MakeSubstituteIndex(new List<EventPageSearchItem>
{
new EventPageSearchItem(){Path = "/good/path/folder1/item1", TemplateId = _eventTemplateId },
new EventPageSearchItem{Path="/good/path/folder2/item2", TemplateId = _eventTemplateId},
new EventPageSearchItem{Path="/bad/path/folder2/item2", TemplateId = _eventTemplateId}
});
//act
var provider = new UpcomingEventProvider(index);
var events = provider.GetEvents(3, "/good/path");
//assert
Assert.That(events.ToList(), Has.Count.EqualTo(2));
}
private static ISearchIndex MakeSubstituteIndex(List<EventPageSearchItem> itemsToReturn)
{
ISearchIndex index = Substitute.For<ISearchIndex>();
_eventTemplateId = MyProject.Library.IEvent_PageConstants.TemplateId;
SimpleFakeRepo<EventPageSearchItem> repo = new SimpleFakeRepo<EventPageSearchItem>(itemsToReturn);
index.CreateSearchContext().GetQueryable<EventPageSearchItem>().Returns(repo);
return index;
}
public class SimpleFakeRepo<T> : EnumerableQuery<T>
{
public SimpleFakeRepo(IEnumerable<T> enumerable)
: base(enumerable)
{}
}
And this is my logic under test:
public class EventPageSearchItem: SearchResultItem
{
}
public interface IUpcomingEventProvider
{
IEnumerable<EventPageSearchItem> GetEvents(int numberOfEvents, string rootItemPath);
}
public class UpcomingEventProvider : IUpcomingEventProvider
{
private readonly ISearchIndex _searchIndex;
public UpcomingEventProvider(ISearchIndex searchIndex)
{
_searchIndex = searchIndex;
}
public IEnumerable<EventPageSearchItem> GetEvents(int numberOfEvents, string rootItemPath)
{
var ctx = _searchIndex.CreateSearchContext();
var queryable = ctx.GetQueryable<EventPageSearchItem>();
return queryable
.Where(item =>
item.TemplateId== MyProject.Library.IEvent_PageConstants.TemplateId
&& item.Path.StartsWith(rootItemPath)).Take(numberOfEvents);
}
}
}
This is failing with this stack trace:
System.ArgumentNullException : Value cannot be null.
Parameter name: arguments
at System.Linq.Expressions.Expression.RequiresCanRead(Expression expression, String paramName)
at System.Linq.Expressions.Expression.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi)
at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments)
at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression[] arguments)
at System.Linq.Queryable.Where[TSource](IQueryable`1 source, Expression`1 predicate)
at MyProject.UnitTests.ComponentUtilityTests.UpcomingEventProvider.GetEvents(Int32 numberOfEvents, String rootItemPath)
at MyProject.UnitTests.ComponentUtilityTests.UpcomingEventProviderTests. System.ArgumentNullException : Value cannot be null.
If I modify the Linq expression to include AsEnumerable(), everything passes, but this will prevent the Linq from generating an expression tree for the index provider.
return queryable.AsEnumerable().Where( etc. )
Upvotes: 3
Views: 1681
Reputation: 1580
I'm not sure about the differences between your mocking frameworks, but I notice that Stephen's code explicitly sets up a fake return value for index.CreateSearchContext(). Your code does not as far as I can see. That is the only notable difference I can see between your code and his.
A similar error message here also indicates that this error message can come from a lack of recursive mock support: Rhino Mock Entity Framework using UnitofWork Pattern not working
Upvotes: 1