Stefan Forsberg
May 7, 2010
  4088
(1 votes)

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.

PageingFakeData

 

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

May 07, 2010

Comments

Joel Abrahamsson
Joel Abrahamsson Sep 21, 2010 10:33 AM

Great post Stefan!

I would love to see a follow up where you combine this with StructureMap profiles creating a lifecycle aware site.

Please login to comment.
Latest blogs
Optimizely SendGrid SMTP host is deprecated

SendGrid is a services for sending email that is included in Optimizely DXP. Previously smtp.episerver.net was the recommended SMTP server to use,...

Tomas Hensrud Gulla | Dec 4, 2022 | Syndicated blog

Hosting Optimizely CMS 12 on Docker Engine

Since Optimizely CMS can now be deployed as a Docker container, here is a demonstration of building, running and scaling an Optimizely CMS 12 site ...

Stefan Holm Olsen | Dec 4, 2022 | Syndicated blog

How to use CacheTagHelper with content areas in Optimizely CMS 12

I might be going out on a limb here - if you have a better solution, feel very free to share it!  Upgrading your Optimizely web application from .N...

Andreas J | Dec 2, 2022

The 1001st Piece in your 1000 Piece Puzzle: .NET Default Interface Functions

I was recently working with a client who wanted a reasonably large subsystem added to Optimizely that would add automated management to their...

Greg J | Nov 28, 2022 | Syndicated blog

Video Demonstration, creating a CMS12 Alloy Sample Site

Hey All Below you will find a quick video demonstration on how to install a local version of Alloy Sample based on CMS12 / .Net 6. As you will see ...

Minesh Shah (Netcel) | Nov 28, 2022

How to create an admin user I Optimizely CMS – with Episerver CLI

In this blog post I’ll show how to create an admin user for Optimizely CMS in a new environment where you don’t have access to the admin interface.

Ove Lartelius | Nov 28, 2022 | Syndicated blog