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.
ContextService.Get().Push(pageContext);
and what should happen after. Please extend the question.