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.