Skip to content

Instantly share code, notes, and snippets.

@viviandeveloper
Last active October 20, 2023 10:33
Show Gist options
  • Save viviandeveloper/1a632d8bfb8fa62e23c4679b086728ce to your computer and use it in GitHub Desktop.
Save viviandeveloper/1a632d8bfb8fa62e23c4679b086728ce to your computer and use it in GitHub Desktop.
Solution to the wretched "There is no method 'GetResults' on type 'Sitecore.ContentSearch.Linq.QueryableExtensions' that matches the specified arguments" error when unit testing search. Sitecore is tightly coupled to their own implemention of IQueryProvider which contains an 'Execute' method not found on standard .Net implementions.
namespace Testing
{
public class NewsController_LatestNews
{
[Theory]
[AutoData]
public void Should_be_news_items_ordered_by_date_descending(IEnumerable<NewsResultItem> newsResultItems)
{
var searchContext = Substitute.For<IProviderSearchContext>();
var queryable = new LuceneProviderQueryableStub(enumerable);
searchContext.GetQueryable().Returns(queryable);
var newsController = new NewsController(indexName => searchContext);
var actual = newsController.LatestNews(5).Model;
Assert.IsGreater(actual.First().Date, actual.Last().Date);
}
}
// Decorator that delegates querying to 'System.Linq.EnumerableQuery'
// and provides the missing execute method Sitecore requires.
public class LuceneProviderQueryableStub<TElement> : IOrderedQueryable<TElement>, IOrderedQueryable, IQueryProvider
{
private readonly EnumerableQuery<TElement> innerQueryable;
public Type ElementType { get { return ((IQueryable)innerQueryable).ElementType; } }
public Expression Expression { get { return ((IQueryable)innerQueryable).Expression; } }
public IQueryProvider Provider { get { return this; } }
public LuceneProviderQueryableStub(IEnumerable<TElement> enumerable)
{
innerQueryable = new EnumerableQuery<TElement>(enumerable);
}
public LuceneProviderQueryableStub(Expression expression)
{
innerQueryable = new EnumerableQuery<TElement>(expression);
}
public IQueryable CreateQuery(Expression expression)
{
return new LuceneProviderQueryableStub<TElement>((IEnumerable<TElement>)((IQueryProvider)innerQueryable).CreateQuery(expression));
}
public IQueryable<TElement1> CreateQuery<TElement1>(Expression expression)
{
return (IQueryable<TElement1>)new LuceneProviderQueryableStub<TElement>((IEnumerable<TElement>)((IQueryProvider)innerQueryable).CreateQuery(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
var items = this.ToArray();
object results = new SearchResults<TElement>(items.Select(s => new SearchHit<TElement>(0, s)), 0);
return (TResult)results;
}
public IEnumerator<TElement> GetEnumerator()
{
return ((IEnumerable<TElement>)innerQueryable).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class NewsController
{
private readonly Func<string, IProviderSearchContext> searchContextFactory;
public NewsController(Func<string, IProviderSearchContext> searchContextFactory)
{
this.searchContextFactory = searchContextFactory;
}
public ViewResult LatestNews(int count)
{
using (var searchContext = searchContextFactory("news_index"))
{
var newsResults = searchContext.GetQueryable<NewsResultItem>().OrderByDescending(x => x.Date).Take(count).GetResults();
return View("~/Views/LatestNews.cshtml", newsResults);
}
}
}
}
@viviandeveloper
Copy link
Author

what is enumerable here?
var queryable = new LuceneProviderQueryableStub(enumerable);

Sorry very late coming back to this one. Probably a typo, I think it was supposed to be the newsResultItems argument.

Also worth checking out is this guy's repo https://github.com/spinico/MethodRedirect

You can even redirect a static method e.g. Queryable.Where<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> expression) to your own stub implementation, although it requires a bit of tinkering to get working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment