I quite often get questions on how we deal with testing and inversion of control in my current web forms project running on EPiServer CMS 6. I wrote an article about it when I first started working with it, and feel that maybe it is time to add some supplemental notes on how to include EPiServer in the mix. If you are interested in something more concrete I suggest having a look at Mattias Johansson’s and Joel Abrahamsson’s open source EPiMVP project.
The heavy lifting was already done by the time I entered the team by my current and former collegues; I believe mostly Patrik Akselsson as well as Karl Ahlin, Oskar Bejbom and Joakim Molin. Please correct me if I’m wrong.
Basically, what you need to do is use EPiServer and PageTypeBuilder base files for inheritance rather than what is used in the previously mentioned sample project. Our first goal is to make EPiServer’s CurrentPage object easily available in our Presenter; so, starting with the old sample project, you would need to install the NuGet packages and create a simple StartPage page type using PTB.
[PageType("A6CB556A-E5C1-4C3D-97B8-F682F9C7D558", Name = "Start page", Description = "This is a start page", Filename = "/Default.aspx", SortOrder = 5 )] public class StartPage : PageTypeBuilder.TypedPageData { [PageTypeProperty( Type = typeof(PropertyString), EditCaption = "Text", HelpText = "Some text", SortOrder = 10)] public virtual string Text { get; set; } }
What we want to accomplish is to have the typed EPiServer CurrentPage object available directly on the injected View (line 16 below), so we easily can access the properties on it. So in the example it would be our StartPage.
public class StartPagePresenter : PresenterBase { private readonly IStartPageView _view; private readonly IImportantService _importantService; public StartPagePresenter(IStartPageView view, IImportantService importantService) { if (view == null) throw new ArgumentNullException("view"); if (importantService == null) throw new ArgumentNullException("importantService"); _view = view; _importantService = importantService; } // .. public string GetText(bool useService) { var text = _view.CurrentPage.Text; return useService ? _importantService.GetImportantText(text) : "No text"; }
To achieve this the base IView interface would need a bit of altering so that it takes a PageData type object; this allows us to pass our StartPage type whilst creating our IStartPageView view.
public interface IView<T> where T : PageData { T CurrentPage { get; }
public interface IStartPageView : IView<StartPage> { string ImportantHeading { get; set; } string ImportantText { get; set; } }
You would also need to alter the old PresentedPageBase class to accept the new page type. This is a bit clearer if you create a TemplatePageBase in which you resolve the current page. You would also need to do the same think in the old PresentedControlBase should you want to access the CurrentPage inside your control presenters.
public abstract class PresentedPageBase<TPresenter, TPage> : TemplatePageBase<TPage>, IView where TPresenter : PresenterBase where TPage : PageData
public abstract class TemplatePageBase<TPage> : TemplatePage where TPage : PageData { public new TPage CurrentPage { get { return this.CurrentPage<TPage>(); } }
public static T CurrentPage<T>(this IPageSource page) where T : PageData { return IoC.Get<ICurrentPageResolver>().CurrentPage<T>(page); }
An extended copy of the old example is available at GitHub should you be interested. It is not 100% complete in the unit testing department, but it will give you the basic idea.