How do you setup different states that depends on EpiServer page structure?
Let’s say you have a page that acts as a container for news. The page simply lists all it’s children in a PageList. Now our GUI developer want us to have at least 21 pages to that they can check that the html they wrote for the paging functionality behaves ok. Soon after you need to verify that if the container doesn’t contain any children at all a message is displayed to inform the user that no news were found.
While these examples might seem somewhat silly the thing I’m getting at is that sometimes you (or your team) need your page structure to be in a certain state for you to verify and or test something.
Alternatives
So, what options do we have today to accomplish this?
1. The manual approach
This is basically just going into edit mode and create whatever structure(s) you need to verify your testing.
2. Creating lists programmatically
In the above examples we don’t really care what data is in the news items them self so we can create lists specifically for the test scenarios . Since we’re using a PageList we do have to make sure that we’re using a PageDataCollection and that the pages are correctly setup to not be filtered by FilterForVisitor.
3. Hooking up to DataFactory.LoadedChildren
This event enables us to change the resulting PageDataCollection. This results in much the same approach as number 2 above but keep in mind that this will also affect the way that the Episerver tree itself is generated too.
4. Using a custom page type builder.
This enables us to create a whole structure that behaves exactly like ordinary pages without them actually existing.
So how do these techniques measure up?
My problem with 1-3 is that modifying your “production” code and setup the state for your tests there almost always leads to a mess. You get those if debug statements (or even worse, code you have to remember to uncomment before deploying) and all in all it just feels fragile. Alternative 1 also can be tricky to do in a unit testing scenario since it can require manual fixing, which probably means that those test won’t run / pass very often.
Alternative 4 can be a good solution but it has some drawbacks. Depending on how much and what you want to test it might be slightly overkill to implement. A custom page provider also requires a enterprise license, which probably isn’t a problem in your local development but if you for some reason would like to use your faked structure on the live server (yes I know this is highly unlikely) you might not be able to do so. The final drawback is somewhat of a nitpick for me, but to use this in your unit tests you have to setup the whole configuration for episerver in your tests. Sure, it doesn’t take a trip to the database, but still.
Using DI and PTB 1.2
One of the new features in PageTypeBuilder 1.2 is the ability to inject dependencies into your TypedPageData classes. Joel has written a post about this here. I’ve done some simple proof on concepting about using this to setup structure, so please take the code for what it is.
To be able to controll the fetching of child pages we inject a dependency to IPageSource. Using the example above out news container could look like this
1: [PageType("345D0610AE114BC5B5AAE8450D364757", Name = "News Container", Filename = "~/UI/Pages/NewsContainer.aspx")]
2: public class NewsContainer : TypedPageData
3: {
4: private readonly IPageSource pageSource;
5:
6: public NewsContainer(IPageSource pageSource)
7: {
8: this.pageSource = pageSource;
9: }
10:
11: public List<NewsItem> GetNews()
12: {
13: return pageSource
14: .GetChildren(PageLink)
15: .OfType<NewsItem>()
16: .ToList();
17: }
18: }
We can now create “fake” classes to use for when we actually have the need to see the pages (for instance the scenario with paging described in the beginning of the post).
1: public class FakeData : IPageSource
2: {
3: public PageDataCollection GetChildren(PageReference pageLink)
4: {
5: var pageTypeID = PageTypeResolver.Instance.GetPageTypeID(typeof (NewsItem)).Value;
6: var pageType = PageType.Load(pageTypeID);
7:
8: InitializeBaseData initializer = new InitializeBaseData();
9:
10: PageDataCollection pageDataCollection = new PageDataCollection();
11: for (int i = 0; i < 50; i++)
12: {
13: var newsItem = new NewsItem { MainBody = "Hello" };
14: initializer.InitializePageData(newsItem, "SomePageName" + i, pageType, Guid.NewGuid(), new PageReference(5000 + i), pageLink, new List<string>(), string.Empty);
15: pageDataCollection.Add(newsItem);
16: }
17:
18: return pageDataCollection;
19: }
20:
21: public PageData GetPage(PageReference pageLink)
22: {
23: throw new NotImplementedException();
24: }
25:
26:
27: public PageData CurrentPage
28: {
29: get { throw new NotImplementedException(); }
30: }
31: }
The class InitializeBaseData is more a less a copy of the reflected code from the method InitializePageData in PageProviderBase (minus the UrlSegment part since it’s method GetUrlFriendlySegment for some reason is internal). The reason for doing this is to make sure the PageData part of the TypedPageData has values to be able to use it with the PageList controll.
In our unit tests we can simply mock the IPageSource interface to test whatever scenario we want to test.
1: [TestMethod]
2: public void Some_pretty_redundant_test_that_doesnt_really_test_anything()
3: {
4:
5: var pageSourceMock = new Moq.Mock<IPageSource>();
6: pageSourceMock.Setup(x => x.GetChildren(It.IsAny<PageReference>())).Returns(new PageDataCollection());
7: NewsContainer container = new NewsContainer(pageSourceMock.Object);
8:
9: Assert.IsTrue(container.GetNews().Count == 0);
10: }
Hopefully I’ll have time to come back to this subject in a later post.
xoxo
Great post Stefan!
I would love to see a follow up where you combine this with StructureMap profiles creating a lifecycle aware site.