Skip to content

Instantly share code, notes, and snippets.

@nickwesselman
Last active January 26, 2024 19:33
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nickwesselman/04c6f35beb6aac2a540fb69c118d2304 to your computer and use it in GitHub Desktop.
Save nickwesselman/04c6f35beb6aac2a540fb69c118d2304 to your computer and use it in GitHub Desktop.
Unit Testing Sitecore ContentSearch (LINQ to Sitecore) using Moq
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Sitecore.Abstractions;
using Sitecore.ContentSearch;
using Sitecore.Data.Items;
namespace BasicCompany.Feature.Products.Services
{
public class ProductRepository : IProductRepository
{
protected readonly BaseFactory Factory;
protected readonly BaseItemManager ItemManager;
public ProductRepository(BaseFactory factory, BaseItemManager itemManager)
{
Debug.Assert(factory != null);
Debug.Assert(itemManager != null);
Factory = factory;
ItemManager = itemManager;
}
public IEnumerable<Item> GetProducts(Item parent)
{
using (var context = GetSearchContext(parent))
{
var results = context.GetQueryable<ProductSearchQuery>()
.Where(product => product.Paths.Contains(parent.ID) && product.Templates.Contains(Templates.Product.Id))
.Select(x => new {
Uri = x.UniqueId,
Database = Factory.GetDatabase(x.UniqueId.DatabaseName)
}).ToList();
return results.Select(x => ItemManager.GetItem(x.Uri.ItemID, x.Uri.Language, x.Uri.Version, x.Database));
}
}
protected virtual IProviderSearchContext GetSearchContext(Item item)
{
return ContentSearchManager.GetIndex((SitecoreIndexableItem)item).CreateSearchContext();
}
}
}
using System.Linq;
using BasicCompany.Feature.Products.Services;
using Moq;
using Moq.Protected;
using Sitecore.Abstractions;
using Sitecore.ContentSearch;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Globalization;
using Xunit;
using Version = Sitecore.Data.Version;
namespace BasicCompany.Feature.Products.Tests
{
public class ProductRepositoryTests
{
[Fact]
public void FiltersOnPathAndTemplate()
{
// Mock the database
var db = Mock.Of<Database>();
// Mock the parent item
var parentId = ID.NewID;
var parentDef = new ItemDefinition(parentId, "parent", Templates.Product.Id, ID.Null);
var parentData = new ItemData(parentDef, Language.Parse("en"), Version.Parse(1), new FieldList());
var parent = new Mock<Item>(parentId, parentData, db);
// Mock the Sitecore services
var factoryMock = new Mock<BaseFactory>();
factoryMock.Setup(x => x.GetDatabase(It.IsAny<string>())).Returns(db);
var factory = factoryMock.Object;
var itemManager = Mock.Of<BaseItemManager>();
// Mock search context, so we are testing LINQ to Objects instead of LINQ to Sitecore
var itemUri = new ItemUri("sitecore://master/{11111111-1111-1111-1111-111111111111}?lang=en&ver=1");
var mockSearchContext = new Mock<IProviderSearchContext>();
mockSearchContext.Setup(x => x.GetQueryable<ProductSearchQuery>())
.Returns(new[]
{
// Matches Path and Template
new ProductSearchQuery
{
UniqueId = itemUri,
Templates = new[] {Templates.Product.Id },
Paths = new[] { parentId }
},
// Matches Path and Template
new ProductSearchQuery
{
UniqueId = itemUri,
Templates = new[] { ID.NewID, Templates.Product.Id, ID.NewID },
Paths = new[] { ID.NewID, parentId, ID.NewID }
},
// Matches Template Only
new ProductSearchQuery
{
UniqueId = itemUri,
Templates = new[] { Templates.Product.Id },
Paths = new[] { ID.NewID }
},
// Matches Path Only
new ProductSearchQuery
{
UniqueId = itemUri,
Templates = new[] { ID.NewID },
Paths = new[] { parentId }
}
}.AsQueryable());
// Use Moq.Protected to ensure our test object uses the mock search context
var mockRepo = new Mock<ProductRepository>(factory, itemManager);
mockRepo.Protected()
.Setup<IProviderSearchContext>("GetSearchContext", ItExpr.IsAny<Item>())
.Returns(mockSearchContext.Object);
// Act
var result = mockRepo.Object.GetProducts(parent.Object).ToList();
// If the tested class is filtering appropriately, we should get predictible results
Assert.Equal(2, result.Count);
}
}
}
using System.Collections.Generic;
using Sitecore.ContentSearch;
using Sitecore.Data;
namespace BasicCompany.Feature.Products.Services
{
public class ProductSearchQuery
{
[IndexField("_uniqueid")]
public virtual ItemUri UniqueId { get; set; }
[IndexField("_path")]
public virtual IEnumerable<ID> Paths { get; set; }
[IndexField("_templates")]
public virtual IEnumerable<ID> Templates { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment