Creating a unit test for my custom Localization provider.

After creating my Localization provider, I wanted to create some unit test for it. I took Joel Abrahamsson’s  Tests for the EPiServer 7 MVC templates as a starting point.
As this is a localization provider, which uses e.g. strongly typed lookups I needed to add more “Fakes” to the CmsContext, e.g. the ContentTypeRepository and the LocalizationService. I also needed to add more “Fake lookups”. Just look in the code which ones I needed.
To be able to create my ContentTypes I added a helper method.

public void CreatePageType(Type pageType)
            this.nextPageTypeId += 1;

            ContentTypeAttribute attribute =
                   Attribute.GetCustomAttribute(pageType, typeof(ContentTypeAttribute));

            ContentType contentType = A.Fake();
            contentType.ID = this.nextPageTypeId;
            contentType.ModelType = pageType;
            contentType.Name = pageType.Name;
            contentType.GUID = attribute.GetGUID().GetValueOrDefault();
            contentType.Description = attribute.Description;
            contentType.DisplayName = attribute.DisplayName;
            contentType.IsAvailable = attribute.AvailableInEditMode;
            contentType.GroupName = "UnitTests";
            contentType.SortOrder = 100;
            contentType.ACL = new AccessControlList();


            A.CallTo(() => this.ContentTypeRepository.Load(pageType.Name))
             .Returns(this.pageTypes.FirstOrDefault(pt => pt.Name.Equals(pageType.Name)));

            A.CallTo(() => this.ContentTypeRepository.Load(pageType)).Returns(this.pageTypes.FirstOrDefault(pt => pt.Name.Equals(pageType.Name)));

So, now I can add my ContentTypes. Next create the fake pages, in this case the translation containers. To be able to do this I created two helper methods that create a basic page and one to create a language version for it.

        public T CreateContent([NotNull] string name, [NotNull] ContentReference parentLink) where T : PageData, new()
            // Get the ContentType for the page you want to create
            ContentType contentType = this.ContentTypeRepository.Load(typeof(T));

            T page = new T();

            page.Property["PageWorkStatus"] = new PropertyNumber((int)VersionStatus.Published);
            page.Property["PageStartPublish"] = new PropertyDate(DateTime.Now.AddDays(-1));
            page.Property["PageName"] = new PropertyString(name);

            page.Property["MasterLanguageBranch"] = new PropertyString(CultureInfo.CurrentUICulture.Name);
            page.Property["PageLanguageBranch"] = new PropertyString(CultureInfo.CurrentUICulture.Name);
            page.Language = new CultureInfo(CultureInfo.CurrentUICulture.Name);

            page.Property["PageParentLink"] = new PropertyPageReference(parentLink);

            page.Property["PageLink"] = new PropertyPageReference();

            page.ContentLink = parentLink == ContentReference.RootPage
                                   ? ContentReference.StartPage
                                   : new PageReference(++this.nextPageId);

            page.ExistingLanguages = new List { new CultureInfo(CultureInfo.CurrentUICulture.Name) };

            // If no ContentType can be found, just create the ContentData without a ContentType
            if (contentType.ID > 0)
                page.Property["PageTypeID"] = new PropertyNumber(contentType.ID);
                page.Property["PageTypeName"] = new PropertyString(contentType.Name);
                page.Property["ContentTypeID"] = new PropertyNumber(contentType.ID);

            this.AddChild(page.ParentLink, page);

            return page;

        public T CreateLanguageVersionOfContent([NotNull] ContentReference contentLink, [NotNull] LanguageSelector languageSelector) where T : PageData, new()
            // Get the original page
            T page =
                    contentLink, new LanguageSelector(CultureInfo.CurrentUICulture.Name)) as T;

            if (page == null)
                throw new ContentNotFoundException();

            // Create a copy
            T languageVersion = page.CreateWritableClone() as T;

            if (languageVersion == null)
                throw new EPiServerException("Creating a copy of the item did not succeed.");

            // Set the language to the new version
            languageVersion.Language = new CultureInfo(languageSelector.LanguageBranch);

            // Set the contentlink to self, as it is a language version
            languageVersion.ContentLink = contentLink;

            // Add new language to the version
            languageVersion.ExistingLanguages = new List
                                                             new CultureInfo(page.LanguageBranch),
                                                             new CultureInfo(

            // Add new language to the original
            page.ExistingLanguages = new List
                                                  new CultureInfo(page.LanguageBranch),
                                                  new CultureInfo(languageSelector.LanguageBranch)

            this.AddChild(page.ParentLink, languageVersion);

            return languageVersion;

As this just takes care of the basic properties, whenever you need more properties you can use these methods to create the base and set the remaining properties later.

        protected static TranslationItem CreateTranslationItem([NotNull] string originalText, [NotNull] string translation, [NotNull] ContentReference parentLink, [NotNull] LanguageSelector languageSelector)
            // Create the base item
            TranslationItem translationItem = CmsContext.CreateContent(
                originalText, parentLink);

            // Set the additional properties for this type.
            translationItem.OriginalText = originalText;
            translationItem.Translation = translation;

            return translationItem;

        protected static TranslationItem AddLanguageVersionToTranslationItem([NotNull] ContentReference contentLink, [NotNull] string translation, [NotNull] LanguageSelector languageSelector)
            // Create the base language version
            TranslationItem translationItem = CmsContext.CreateLanguageVersionOfContent(
                contentLink, languageSelector);

            // Change the properties that need changing for this version.
            translationItem.Translation = translation;

            return translationItem;

This is the plumbing, now on with the actual test. In the specs for the translation tests, I create my ContentTypes, containers, language items and instantiate my localization provider.

        private Establish context = () =>
                CmsContext = new CmsContext();


                LanguageSelector masterLanguageSelector = new LanguageSelector(CmsContext.MasterLanguage.Name);
                LanguageSelector secondLanguageSelector = new LanguageSelector(CmsContext.SecondLanguage.Name);

                CmsContext.CreateContent("StartPage", ContentReference.RootPage);

                ContentReference containerReference = CmsContext.CreateContent("Translations", ContentReference.StartPage).ContentLink;
                CmsContext.CreateLanguageVersionOfContent(containerReference, secondLanguageSelector);

                ContentReference itemReference = CreateTranslationItem("TextOne", "Translation One", containerReference, masterLanguageSelector).ContentLink;
                AddLanguageVersionToTranslationItem(itemReference, "Vertaling Een", secondLanguageSelector);

                ContentReference subContainerReference = CmsContext.CreateContent("SubTranslations", containerReference).ContentLink;

                CreateTranslationItem("SubTextOne", "SubTranslation One", subContainerReference, masterLanguageSelector);

                ContentReference categoryContainerReference = CmsContext.CreateContent("Categories", containerReference).ContentLink;
                CmsContext.CreateLanguageVersionOfContent(categoryContainerReference, secondLanguageSelector);

                ContentReference categoryReference = CreateTranslationItem("CategoryOne", "CategoryTranslation One", categoryContainerReference, masterLanguageSelector).ContentLink;
                AddLanguageVersionToTranslationItem(categoryReference, "CategorieVertaling Een", secondLanguageSelector);

                NameValueCollection configValues = new NameValueCollection { { "containerid", "0" } };

                LocalizationProvider = new TranslationProvider();

                // Instanciate the provider
                LocalizationProvider.Initialize("Translations", configValues);

                // Add it at the end of the list of providers.

Last I added some test scenarios, e.g. using the translate control

    public class Get_a_translation_for_a_translate_control : TranslationSpecs
        /// The result.
       private static string result;

        /// The control
       private static Translate translate = new Translate { Text = "/textone" };
        /// Should be the translated control.
       private Because of = () => result = CmsContext.GetRenderedText(translate);
        /// Should be the translated control.
        private It should_be_the_translated_value = () => result.ShouldEqual("Translation One");

It took some time figuring out what needed to be faked, e.g. a translation for  a category uses  a TryGetExistingInstance to get the provider. So that needed to be faked to.

A.CallTo(() => ServiceLocator.Current.TryGetExistingInstance(out ignored)).Returns(true).AssignsOutAndRefParameters(this.ProviderBasedLocalizationService);

I tried to make my plumbing as generic as possible so I can use it to test more providers, and whatever I may come up with next.

The code can be found on GitHub, together with the provider.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s