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 =
                   (ContentTypeAttribute)
                   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();

            this.pageTypes.Add(contentType);

            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.

[NotNull]
        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;
        }

[NotNull]
        public T CreateLanguageVersionOfContent([NotNull] ContentReference contentLink, [NotNull] LanguageSelector languageSelector) where T : PageData, new()
        {
            // Get the original page
            T page =
                this.ContentRepository.Get(
                    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(
                                                                 languageSelector.LanguageBranch)
                                                         };

            // 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.

[NotNull]
        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;
        }

[NotNull]
        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.

[UsedImplicitly]
        private Establish context = () =>
            {
                CmsContext = new CmsContext();

                CmsContext.CreatePageType(typeof(TranslationContainer));
                CmsContext.CreatePageType(typeof(TranslationItem));
                CmsContext.CreatePageType(typeof(CategoryTranslationContainer));

                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.
                CmsContext.ProviderBasedLocalizationService.Providers.Add(LocalizationProvider);
            };

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

[Subject("Translations")]
    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:

WordPress.com Logo

You are commenting using your WordPress.com 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