Stefan Forsberg
Nov 10, 2010
  3384
(2 votes)

Making EPiServer code testable – the mild mannered rant

As I mentioned in my last post writing tests for your code isn’t hard. The big “but” you know is coming is that this is valid only if the framework you’re writing code for/in is built to be testable. Just as an example, to be able to write the tests I wanted for the scenario outlined in the previous post I had to write this code.

   1: public interface ILanguageManagerFacade
   2:     {
   3:         string TranslateFallback(string key, string fallback);
   4:         string Translate(string key, string language);
   5:     }
   1: public class LanguageManagerFacade : ILanguageManagerFacade
   2:     {
   3:         public string TranslateFallback(string key, string fallback)
   4:         {
   5:             return LanguageManager.Instance.TranslateFallback(key, fallback);
   6:         }
   7:  
   8:         public string Translate(string key, string language)
   9:         {
  10:             return LanguageManager.Instance.Translate(key, language);
  11:         }
  12:     }
   1: public interface IPageLanguageSettingFacade
   2:     {
   3:         PageLanguageSetting Load(PageReference pageReference, string language);
   4:     }
   1: public class PageLanguageSettingFacade : IPageLanguageSettingFacade
   2: {
   3:     public PageLanguageSetting Load(PageReference pageReference, string language)
   4:     {
   5:         return PageLanguageSetting.Load(pageReference, language);
   6:     }
   7: }
   1: public interface IPageDataFromHttpHandlerParser
   2:     {
   3:         PageData GetPage();
   4:     }
   1: public class PageDataFromHttpHandlerParser : IPageDataFromHttpHandlerParser
   2:     {
   3:         private readonly HttpContextBase _httpContext;
   4:  
   5:         public PageDataFromHttpHandlerParser(HttpContextBase httpContext)
   6:         {
   7:             _httpContext = httpContext;
   8:         }
   9:  
  10:         public PageData GetPage()
  11:         {
  12:             var page = _httpContext.Handler as PageBase;
  13:  
  14:             return page.CurrentPage;
  15:         }
  16:     }
   1: public class TranslateWithFallbackLanguageBranch
   2:     {
   3:         private readonly ILanguageManagerFacade _languageManagerFacade;
   4:         private readonly IGetFallbackLanguage _getFallbackLanguage;
   5:  
   6:         public TranslateWithFallbackLanguageBranch(ILanguageManagerFacade languageManagerFacade, IGetFallbackLanguage getFallbackLanguage)
   7:         {
   8:             _languageManagerFacade = languageManagerFacade;
   9:             _getFallbackLanguage = getFallbackLanguage;
  10:         }
  11:  
  12:         public string Translate(string key)
  13:         {
  14:             string fallbackLanguageId = _getFallbackLanguage.GetFallbackLanguage();
  15:  
  16:             return _languageManagerFacade.TranslateFallback(key, _languageManagerFacade.Translate(key, fallbackLanguageId));
  17:         }
  18:     }
   1: public interface IGetFallbackLanguage
   2:     {
   3:         string GetFallbackLanguage();
   4:     }
   1: public class GetFallbackLanguageFromRootBasedOnCurrentPage : IGetFallbackLanguage
   2:     {
   3:         private readonly IPageLanguageSettingFacade _pageLanguageSettingFacade;
   4:         private readonly IPageReferenceFacade _pageReferenceFacade;
   5:         private readonly IPageDataFromHttpHandlerParser _pageDataFromHttpHandlerParser;
   6:  
   7:         public GetFallbackLanguageFromRootBasedOnCurrentPage(IPageLanguageSettingFacade pageLanguageSettingFacade, IPageReferenceFacade pageReferenceFacade, IPageDataFromHttpHandlerParser pageDataFromHttpHandlerParser)
   8:         {
   9:             _pageLanguageSettingFacade = pageLanguageSettingFacade;
  10:             _pageReferenceFacade = pageReferenceFacade;
  11:             _pageDataFromHttpHandlerParser = pageDataFromHttpHandlerParser;
  12:         }
  13:  
  14:         public string GetFallbackLanguage()
  15:         {
  16:             // Get PageBase using the current handler
  17:             var page = _pageDataFromHttpHandlerParser.GetPage();
  18:  
  19:             // Get the page language setting from the root page, using the current page's language
  20:             // This is where the replacement and fallback languages are defined
  21:             PageLanguageSetting pageLanguageSetting = _pageLanguageSettingFacade.Load(_pageReferenceFacade.RootPage, page.LanguageID);
  22:  
  23:             if (pageLanguageSetting != null)
  24:             {
  25:                 // Check if there actually is a fallback language defined
  26:                 // If so, use the first one
  27:                 if (pageLanguageSetting.LanguageBranchFallback.Length > 0)
  28:                     return pageLanguageSetting.LanguageBranchFallback[0];
  29:             }
  30:  
  31:             // If there is no fallback language defined then we return the current page's language ID
  32:             return page.LanguageID;
  33:         }
  34:     }

I also made use of some EPiAbstraction classes.

The code above is not too interesting but will be discussed in a later post. The point I’m trying to make is that you often have to spend a lot more time on making _episerver_ code testable than writing the tests themselves. This sort of makes the case of the “testing is easy” argument a hard sell.

 

Some closing thoughts

In all fairness (and please correct me if I’m way off base here) I don’t think the EPiServer team in the past has had too many requests from developers to focus on this area. I know that I personally didn’t care too much about it 3 years ago before I was introduced to the involved concepts. But I feel that as the sites we develop get more and more complex being able to, for instance, develop using TDD is becoming a must have.

So, here’s hoping for a more testable future.

Nov 10, 2010

Comments

Kjetil Simensen
Kjetil Simensen Nov 11, 2010 07:52 AM

Hi,

In our current project we're introducing unit testing and we're finding it very useful. but we have some difficulties testing UnifiedFiles and HostingEnvironment related code. Have you had any experience testing this?

-Kjetil

Stefan Forsberg
Stefan Forsberg Nov 11, 2010 08:15 AM

Hello Kjetil,
There's a ready-to-use abstraction/wrapper for UnifiedFile available in epiabstraction (http://epiabstractions.codeplex.com/) so that should save you from having to create it yourself.

Other than that I find it's easier to discuss this when I know more about what you're trying to do, what business requirements you have etc. Feel free to contact me at stefan dot forsberg at cloudnine dot se.

Kjetil Simensen
Kjetil Simensen Nov 11, 2010 08:35 AM

Ah, we must have missed that part of the epiabstraction! We'll have a close look now that we know it's should be in there somewhere :)

Thanks, will look into it and see if it solves our problems.

-Kjetil

Nov 11, 2010 11:04 AM

Nice. Looking forward to the next post!

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