Sitecore 7: A developers quest to mastering unit testing

31
And how I went through the 4 stages of developer denial Prepared by: Jason St-Cyr

Transcript of Sitecore 7: A developers quest to mastering unit testing

Page 1: Sitecore 7: A developers quest to mastering unit testing

And how I went through the 4 stages of developer denial

Prepared by: Jason St-Cyr

Page 2: Sitecore 7: A developers quest to mastering unit testing
Page 3: Sitecore 7: A developers quest to mastering unit testing

Microsoft Fakes

Page 4: Sitecore 7: A developers quest to mastering unit testing
Page 5: Sitecore 7: A developers quest to mastering unit testing

Sitecore Dev team unit testing prototype:

https://github.com/Sitecore/sitecore-seven-unittest-example

Nextdigital blog: Shooting for the Sky:

http://www.nextdigital.com/voice/shooting-for-the-sky

Stackoverflow: Unit Testing IQueryable operations:

http://stackoverflow.com/questions/20863942/unit-testing-iqueryable-operations

Page 6: Sitecore 7: A developers quest to mastering unit testing
Page 7: Sitecore 7: A developers quest to mastering unit testing
Page 8: Sitecore 7: A developers quest to mastering unit testing

Stage 1: Egotism

Page 9: Sitecore 7: A developers quest to mastering unit testing
Page 10: Sitecore 7: A developers quest to mastering unit testing
Page 11: Sitecore 7: A developers quest to mastering unit testing

Stage 2: Confusion

Page 12: Sitecore 7: A developers quest to mastering unit testing
Page 13: Sitecore 7: A developers quest to mastering unit testing
Page 14: Sitecore 7: A developers quest to mastering unit testing

Sitecore Resources…

• Sitecore Dev team unit testing prototype: https://github.com/Sitecore/sitecore-seven-unittest-example

From the community…

• Nextdigital blog: Shooting for the Sky:http://www.nextdigital.com/voice/shooting-for-the-sky

• Stackoverflow: Unit Testing IQueryable operations:http://stackoverflow.com/questions/20863942/unit-testing-iqueryable-operations

Page 15: Sitecore 7: A developers quest to mastering unit testing
Page 16: Sitecore 7: A developers quest to mastering unit testing

[TestMethod]public void GetAll(){

//A small data set of entities with different Template IDsvar data = new List<SearchResultItem>(){

new CaseStudy { TemplateId = CaseStudy.TemplateId, Title = "Match 1", TeaserText = "Match 1 is awesome.", Name = "Match-1" },new CallToActionGeneral { TemplateId = CallToActionGeneral.TemplateId, Content = "This is not a match. This is a call to action", Name = "Not-a-match" },new CaseStudy { TemplateId = CaseStudy.TemplateId, Title = "Match 2", TeaserText = "Match 2 is kind of neat.", Name = "Match-2" },

};

//Create the fake index that will be used to replace the standard Sitecore indexvar index = CreateFakeIndex<SearchResultItem>(data);

//Create the provider that will be testedvar provider = new CaseStudyProvider(index);

//Test the GetAll method, ensuring we return all possible results.var caseStudies = provider.GetAll(data.Count);

//Verify that only 2 of the 3 returned.Assert.AreEqual(caseStudies.Count(), 2);

//Verify that the 2 returned are the correct onesforeach (CaseStudy caseStudy in caseStudies){

var isMatch1 = caseStudy.Name != null && caseStudy.Name.ToLower().Equals("match-1");var isMatch2 = caseStudy.Name != null && caseStudy.Name.ToLower().Equals("match-2");

Assert.IsTrue(isMatch1 || isMatch2, "Result {0} is not expected.", caseStudy.Name);}

}

Test Data

Faking the Index

Running the test

Page 17: Sitecore 7: A developers quest to mastering unit testing

public class CaseStudyProvider : ICaseStudyProvider{

private ISearchIndex Index { get; set; }

public CaseStudyProvider(ISearchIndex index) {Index = index;

}

/// <summary>/// Searches the index for components that are of type 'Case Study' and returns the entity/// </summary>/// <param name="numberOfStudies">The maximum number of studies to return</param>/// <returns>A list of CaseStudy entities</returns>public IEnumerable<CaseStudy> GetAll(int numberOfStudies){

//Get the queryable that will be used to get the case studiesvar context = Index.CreateSearchContext();var queryable = context.GetQueryable<SearchResultItem>();

return queryable.Where(item => item.TemplateId == CaseStudy.TemplateId).Take(numberOfStudies).Cast<CaseStudy>();}

}

Based on the UpcomingEventsProvider by Dan Solovay in

his Stack Overflow post.

Simplified to only return the items in the index

with the correct Template ID.

Page 18: Sitecore 7: A developers quest to mastering unit testing

Sitecore Example Fixture (FakeItEasy):[SetUp]public void Setup(){

this.fakeIndex = A.Fake<ISearchIndex>();this.fakeSearchContext = A.Fake<IProviderSearchContext>();

A.CallTo(() => this.fakeIndex.CreateSearchContext(SearchSecurityOptions.DisableSecurityCheck)).Returns(this.fakeSearchContext);}

Dan Solovay Example (NSubstitute):private static ISearchIndex MakeSubstituteIndex(List<EventPageSearchItem> itemsToReturn){

ISearchIndex index = Substitute.For<ISearchIndex>();_eventTemplateId = MyProject.Library.IEvent_PageConstants.TemplateId;

SimpleFakeRepo<EventPageSearchItem> repo = newSimpleFakeRepo<EventPageSearchItem>(itemsToReturn);index.CreateSearchContext().GetQueryable<EventPageSearchItem>().Returns(repo);return index;

}

Page 19: Sitecore 7: A developers quest to mastering unit testing

• My good buddy Joe comes by…

• And then I’m all like…

Page 20: Sitecore 7: A developers quest to mastering unit testing

Stage 3: Frustration

Page 21: Sitecore 7: A developers quest to mastering unit testing
Page 22: Sitecore 7: A developers quest to mastering unit testing

/// <summary>/// The Fake Provider Search Context will always return the repository assigned to it/// </summary>public class FakeProviderSearchContext : IProviderSearchContext{

/// <summary>/// Storage for repository used by this instance of the context/// </summary>public IQueryable Repository { get; set; }

/// <summary>/// Returns the repository/// </summary>/// <typeparam name="TItem"></typeparam>/// <returns></returns>public IQueryable<TItem> GetQueryable<TItem>() where TItem : new(){

return Repository.Cast<TItem>();}

Storing data

Faking Data Return

Right?

Page 23: Sitecore 7: A developers quest to mastering unit testing
Page 24: Sitecore 7: A developers quest to mastering unit testing

• At some point during the flow, even if explicitly set, the search context object was going null.

• That means no results.

• …no success.

• …full of anger.

• …desire to push forward, so close.

Page 25: Sitecore 7: A developers quest to mastering unit testing

Stage 4: Success

Page 26: Sitecore 7: A developers quest to mastering unit testing

Doesn’t

work

Fake

Context

Fake

Index

Page 27: Sitecore 7: A developers quest to mastering unit testing

Fake

Index

Fake

ContextWorks!

Page 28: Sitecore 7: A developers quest to mastering unit testing

/// <summary>/// Represents the fake index. Can be bound to a repository so that /// all search contexts created will bind to this repository./// </summary>public class FakeSearchIndex : ISearchIndex{

/// <summary>/// Storage for the repository data used on the index/// </summary>public IQueryable Repository { get; set; }

/// <summary>/// Implement a fake search context creation which will allow us to persist/// a fake data associated to the index/// </summary>/// <param name="options"></param>/// <returns></returns>public IProviderSearchContext CreateSearchContext(SearchSecurityOptions options = SearchSecurityOptions.EnableSecurityCheck){

var context = new FakeProviderSearchContext();context.Index = this;context.SecurityOptions = options;context.Repository = Repository;

return context;}

Store the data

Create each context

with the data

Page 29: Sitecore 7: A developers quest to mastering unit testing

/// <summary>/// Create a fake index that contains the data specified/// </summary>/// <param name="items"></param>/// <returns></returns>private static ISearchIndex CreateFakeIndex<T>(List<T> items) where T:SearchResultItem{

var repository = new FakeRepository<T>(items);

var index = new FakeSearchIndex();index.Repository = repository;

return index;} Set the data

Once the data was moved to the Index object,

each context was successfully created, and the

test passed!

Page 30: Sitecore 7: A developers quest to mastering unit testing

• A single FakeSearchIndex class can represent any index.

• It only takes about 20 lines of actual code to implement the fake objects for the index, context, and repository.

• Need to have unit tests create sample data to fill the fake index.

• Sitecore.ContentSearch and Sitecore.ContentSearch.SearchTypes are your namespace friends.

• No mocking needed to test business logic that queries indexes!

- Limited use: can only support testing queries on the context, unless the rest of the ISearchIndex implementation is built out.

Page 31: Sitecore 7: A developers quest to mastering unit testing

Jason St-Cyr

Solution Architect, nonlinear digital

Background in .NET software development and Application Lifecycle

Management

Contact me: [email protected]

Around the web:

Technical Blog:

http://theagilecoder.wordpress.com

LinkedIn Profile:

http://www.linkedin.com/pub/jason-st-cyr/26/a73/645

Nonlinear Thinking:

http://blog.nonlinearcreations.com/en/author/jason-st-cyr/

Twitter: @AgileStCyr