5

I'm using FakeDB to try and set up unit tests for a custom component.

While trying to run it, I walked into a couple of missing .dll's which I hadn't expected since the FakeDB install page hadn't mentioned them, but I added them to the references anyway, reasoning this is probably due to SXA. They were System.Web.MVC (logical I guess) and Sitecore.XA.Foundation.IoC (this is where it fails).

The latter one now throws an InvalidOperationException, telling me

No locator provider is configured at Sitecore.XA.Foundation.IoC.ServiceLocator.get_Current() at Sitecore.XA.Foundation.Mvc.Repositories.Base.ModelRepository.get_Rendering() at Sitecore.XA.Foundation.Mvc.Repositories.Base.ModelRepository.FillBaseProperties(Object m)

I'm aware FakeDB allows me to configure settings that may resolve this, but SXA documentation is still so scarce I couldn't find what to do. Can any of you help me figure this out?

Here's my code:

public class ReadSpeakLinkRepository : ModelRepository, IReadSpeakLinkRepository
{
    public override IRenderingModelBase GetModel()
    {
        ReadSpeakLinkModel model = new ReadSpeakLinkModel();
        FillBaseProperties(model); // test fails here
        if (model.DataSourceItem == null || model.DataSourceItem.TemplateID != SitecoreItemConstants.TemplateId)
        {
            return null;
        }

        FillExtraProperties(model);
        return model;
    }

    private void FillExtraProperties(ReadSpeakLinkModel model)
    {

        var options = LinkManager.GetDefaultUrlOptions();
        options.AlwaysIncludeServerUrl = true;
        model.CurrentUrl = LinkManager.GetItemUrl(Context.Item, options);
        model.ReadText = model.DataSourceItem[SitecoreItemConstants.TextFieldSelector];
        model.ReadTitle = model.DataSourceItem[SitecoreItemConstants.TitleFieldSelector];
    }
}

And the test itself:

    private ReadSpeakLinkRepository repository;
    private Db fakeDb;

    [TestInitialize]
    public void Initialize()
    {
        fakeDb = new Db();
        repository = new ReadSpeakLinkRepository();
    }

    [TestMethod]
    public void TestMethod1()
    {
        fakeDb.Add(new DbItem("ReadSpeakLink") {
            { SitecoreItemConstants.TitleFieldSelector, "titel" },
            { SitecoreItemConstants.TextFieldSelector, "tekst" }
        });

        var result = repository.GetModel();

        Assert.IsNotNull(result);
    }

// plus IDisposable implementation
5

You are using SXA ModelRepository which is using dependency injection and service locator pattern to inject for example:

  • wrapper for Rendering
  • wrapper PageContext
  • ContentRepository
  • few more things...

Now if you want to test you first need to register those in the container and configure that container to be used (of course if you are not using certain wrapper implicity then null will be resolved but as you are not using it that should be a problem).

Looking at your stack trace I see that Rendering property is used: Sitecore.XA.Foundation.Mvc.Repositories.Base.ModelRepository.get_Rendering() so you need to register it into the container.

Take a look bellow at few examples:

Example base method which is setting up container and registering mock of IContentRepository (it's perfect method to be placed in base class for your tests):

protected virtual void SetupServices(Db fakeDb)
{
    var serviceCollection = new ServiceCollection();
    new DefaultSitecoreServicesConfigurator().Configure(serviceCollection);

    serviceCollection.AddTransient(provider => MockContentRepository(fakeDb));

    IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
    ServiceLocator.SetServiceProvider(serviceProvider);
    var instance = typeof(FieldTypeManager).GetField("Instance", BindingFlags.Static | BindingFlags.NonPublic);
    var instanceValue = instance.GetValue(null) as LazyResetable<Sitecore.Abstractions.BaseFieldTypeManager>;
    instanceValue.Value.Initialize();
}

Example how you can mock ContentRepository with NSubstitute:

IContentRepository contentRepository = Substitute.For<IContentRepository>();
contentRepository.GetItem(Arg.Any<ID>()).Returns(info => fakeDb.GetItem(info.Arg<ID>()));

And this is how you can mock for example PageContext:

protected override void SetupServices(Db fakeDb)
{
    base.SetupServices(fakeDb);

    IPageContext pageContextMock = Substitute.For<IPageContext>();
    pageContextMock.Current.Returns(info => fakeDb.GetItem(ID_OF_YOUR_FAKE_PAGE));

    ServiceLocator.ServiceProvider.GetService<IPageContext>().Returns(pageContextMock);
}

And finally an example how you can mock Rendering:

IRendering renderingMock = Substitute.For<IRendering>();
renderingMock.Name.Returns("MY_RENDERING_NAME");
renderingMock.DataSource.Returns(HomeId);

serviceCollection.AddTransient(provider => renderingMock);
  • Using your examples still led to the same error, but it did set me on the right path. I'm now using NSubstitute for ILocator and set that as the LocatorProvider. Is this, in your opinion, the correct way to continue? – Mark-Danney Sep 20 '17 at 15:08
  • First code example contains the right way to do this. We are initializing container exactly in that way. If you are still getting the same error then it means that instance of IRendering is not registered in the container. – Dawid Rutkowski Sep 20 '17 at 16:55
0

In the end, the problem was in the difference between Sitecore.DependencyInjection and Sitecore.XA.Foundation.IoC. They're easily mixed up since the entry points have the same name.

Here's the solution I used:

// using Sitecore.XA.Foundation.IoC;
// using Sitecore.XA.Foundation.IoC.Locator;

protected virtual void SetupServices(Db fakeDb)
{
    var mockServiceCollection = new ServiceCollection();
    new Sitecore.DependencyInjection.DefaultSitecoreServicesConfigurator().Configure(mockServiceCollection);

    var rendering = MockRendering(fakeDb);

    mockServiceCollection.AddTransient(provider => MockContentRepository(fakeDb));
    // ...Add more mock services in the way Dawid showed

    // This part was the main difference and what made it instantly work.
    var serviceProvider = mockServiceCollection.BuildServiceProvider();
    ServiceLocator.SetLocatorProvider(new Locator(serviceProvider));

    var instance = typeof(FieldTypeManager).GetField("Instance", BindingFlags.Static | BindingFlags.NonPublic);
    var instanceValue = instance.GetValue(null) as sc.DependencyInjection.LazyResetable<sc.Abstractions.BaseFieldTypeManager>;
    instanceValue.Value.Initialize();
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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