Ultimately it depends on what type of item/class you are passing into your code or logic in order to make it truly "testable" and what that parent child relationship
really is.
Sitecore.FakeDb is a great tool for faking Sitecore data where you are simply not able to avoid proper Sitecore Items
. When you can avoid using Sitecore Items
in your code, when they translate to more generic classes like POCOs or DTOs, you can use mocking frameworks like Moq (but there are literally hundreds of mocking frameworks out there). I had wrote a post a while back covering unit testing and mocking:
The below snippet is an example when you can try and avoid using Items (illustrated using a test around search):
[Test]
[Category("UnitTest")]
public void ExecuteSearchQuery()
{
//the search context most often will just be the sitecore search context
//but could be outside of sitecore
var mockSearchProvider = new Mock<IProviderSearchContext>();
//the search repository consumes the provider which most often
//will be sitecore but could be another provider and another mechanism
var mockSearchRepository = new Mock<ISearchRepository>();
//the search service instance we want to mock
var searchService = new SearchService(mockSearchProvider.Object,
mockSearchRepository.Object);
//mock the repository
var mockResultsList = new List<SearchHit<SearchResult>>();
mockResultsList.Add(new SearchHit<SearchResult>(1, new SearchResult()
{
Content = "test",
CreatedBy = "admin",
CreatedDate = DateTime.MinValue,
DatabaseName = "web",
Datasource = null,
ExcludeFromSearch = false,
FullLinkToItem = "http://linktoitem",
IsPage = true
}));
var mockSearchResults = new SearchResults<SearchResult>(mockResultsList, 1);
mockSearchRepository
.Setup(ss => ss.Search(mockSearchProvider.Object,
It.IsAny<string>(),
It.IsAny<int>(),
It.IsAny<int>()))
.Returns(mockSearchResults);
//test the rest of the search using the mocked provider
var searchResultsDto = searchService.SiteSearch("test", 1, 10);
//assert
searchResultsDto.Hits.Count().Should().Be(1);
}
To summarize the above, the mockSearchProvider
and mockSearchRepository
are proper Sitecore interfaces that have been mocked, the searchService
is the logic we are covering in the test, and the mockResultsList
is a generic List<>
of items that will be used to drive what would normally be the results from something like the Content Search API
.
If you simply can't avoid a direct reference to a Sitecore Item
, you can mock using Sitecore.FakeDb
like @ellele's answer above:
[Test]
[Category("UnitTest")]
public void TestYourClassLogic()
{
//setup
var testArticle1 = new DbItem("testArticle1", ID.NewID, IArticleConstant.TemplateId);
var testArticle2 = new DbItem("testArticle1", ID.NewID, IArticleConstant.TemplateId);
var parent = new DbItem("testArticleSection") { testArticle1, testArticle2 };
var _db = new Db(){parent};
//test the innerds of the YourClass (the class you have your logic in) that actually calls .GetItem...
var _mockSitecoreContext = new Mock<ISitecoreContext>();
_mockSitecoreContext.Setup(sc => sc.Database).Returns(_db.Database);
var yourClass = new YourClass(_mockSitecoreContext.Object);
var result = tagHelper.YourLogicDoSomethingOnThisArticle(testArticle1.ID);
result.Should().Be("ExpectedValue");
//teardown
_db.Dispose();
}
Why did I cover search in this post when that's not the OP's question/situation? It's the thing I see in most sites, with the most complexity and the easiest to relate to. My end goal is to just show that if you can avoid depending on the Item
directly, mocking and unit testing will almost always be easier (but not always unavoidable). The second thing I wanted to illustrate is that in order to really prove things to be "testable" you need to mock Sitecore layers/services/APIs and likely refactor your own code in order to make it testable. Unit testing done right is not an easy checkbox to fill.
I really hope this helps.