1

I'm currently creating a unit test for an action result method:-

public virtual ActionResult MyControllerTest(){
 var pageItem = PageContext.Current.Item
...
 
 return ViewOrErrorResult<string>(null);
}

Unit test goes like this:-

[Theory, AutoData]
public void MyControllerTest_Results(params){
 //assign
 var pageContext = Substitute.For<PageContext>();
 ContextService.Get().Push(pageContext);

 //Act
 ...
 //Assert

}

However, PageContext.Current.Item is always null. Anyone has tried mocking PageContext.Current.Item? Thanks

1
  • Borj, while my answer shows hot to set the item, it would be great to understand what exactly you're trying to test. It's not clear why do you call ContextService.Get().Push(pageContext); and what should happen after. Please extend the question. Jan 19, 2023 at 17:50

1 Answer 1

3

An attempt to utilize static prop PageContext.Current indeed can be cumbersome. While it is an Ambient Context, Sitecore implementation does not let you mock and set it easily. Even if you do, you'll get concurency issues running your tests in parallel.

A better approach would to inject an instance of PageContext in the code where you wanted to use static PageContext.Current prop. The PageContext can be easily mocked and configured. See below.

PageContext.Item property has a public setter. Just set the item there and that's it:

[Theory, AutoNSubstituteData]
public void PageContextItemTest(
    [Substitute] PageContext pageContext,
    Item current)
{
    pageContext.Item = current;
    Assert.Same(current, pageContext.Item); // pass
}

There is a list of customizations that simplify the Item creating using AutoFixture and AutoFixture.AutoNSubstitute libraries:

public class AutoNSubstituteDataAttribute : AutoDataAttribute
{
    public AutoNSubstituteDataAttribute()
        : base(() => new Fixture()
            .Customize(new AutoNSubstituteCustomization())
            .Customize(new DatabaseCustomization())
            .Customize(new ItemCustomization())
            .Customize(new FieldCustomization()))
    {
    }
}

internal class DatabaseCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Inject(Substitute.For<Database>());
    }
}

internal class ItemCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Item>(x =>
            x.FromFactory(() => CreateItem(fixture))
                .OmitAutoProperties()
        );
    }

    private static Item CreateItem(ISpecimenBuilder fixture)
    {
        var item = Substitute.For<Item>(
            fixture.Create<ID>(),
            ItemData.Empty,
            fixture.Create<Database>());
        item.Name.Returns("item_" + fixture.Create<string>());
        item.Children.Returns(Substitute.For<ChildList>(item, new List<Item>()));
        item.Fields.Returns(Substitute.For<FieldCollection>(item));

        var language = Language.Parse("en");
        item.Language.Returns(language);
        item.Languages.Returns(new[] { language });

        var template = Substitute.For<TemplateItem>(Substitute.For<Item>(
            fixture.Create<ID>(),
            ItemData.Empty,
            fixture.Create<Database>()));
        item.Template.Returns(template);

        item.Version.Returns(Version.First);
        item.Versions.Returns(Substitute.For<ItemVersions>(item));
        item.Versions.Count.Returns(1);

        return item;
    }
}

internal class FieldCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Field>(x =>
            x.FromFactory(() => CreateField(fixture))
                .OmitAutoProperties()
        );
    }

    private static Field CreateField(ISpecimenBuilder fixture)
    {
        var item = fixture.Create<Item>();
        var field = Substitute.For<Field>(
            fixture.Create<ID>(),
            item);
        field.Database.Returns(item.Database);

        return field;
    }
}

UPDATE

The customaizations above are samples. They mock items and props and let you extend it further as needed. For instance, if you need some template id, you can extend the ItemCustomization Create method with the following line and generate random id:

item.TemplateID.Returns(fixture.Create<ID>());

If you need a specific template Id in your test, you can set it in the test:

[Theory, AutoNSubstituteData]
public void SetCustomTemplateId(Item item, ID expected)
{
    item.TemplateID.Returns(expected);
    Assert.Same(expected, item.TemplateID);
}

Same about verstions.

2
  • Hello this one works, thanks. however how do you assign values to its other properties such as version and templateID? tried assigning but it says property is ready only. myItem = myItem .Versions[Version.Latest]; -> Versions is null. TemplateID is null as well.
    – Borj
    Jan 19, 2023 at 23:28
  • +1 for highlighting and referencing the article on Ambient Context
    – Mark Cassidy
    Jan 22, 2023 at 10:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.