Как выполнить модульное тестирование действия GlassController, которое использует Sitecore.Context.Item

Я разработчик sitecore и хочу создать образец проекта модульного тестирования спирали sitecore для проверки логики, которую вы видите в методе действия Index() нашего контроллера «ArticleController»:

public class ArticleController : GlassController
{
    public override ActionResult Index()
    {
        // If a redirect has been configured for this Article, then redirect to new location.
        if (Sitecore.Context.Item.Fields[SitecoreFieldIds.WTW_REDIRECT_TO] != null && !string.IsNullOrEmpty(Sitecore.Context.Item.Fields[SitecoreFieldIds.WTW_REDIRECT_TO].Value))
        {
            var link = (LinkField)Sitecore.Context.Item.Fields[SitecoreFieldIds.WTW_REDIRECT_TO];
            if (link != null)
            {
                if (link.IsInternal)
                {
                    return Redirect(Sitecore.Links.LinkManager.GetItemUrl(link.TargetItem));
                }
                else
                {
                    return Redirect(link.Url);
                }
            }
        }

        var model = new ArticleBusiness().FetchPopulatedModel;

        return View("~/Views/Article/Article.cshtml", model);
    }

    //below is alternative code I wrote for mocking and unit testing the logic in above Index() function
    private readonly IArticleBusiness _businessLogic;
    public ArticleController(IArticleBusiness businessLogic)
    {
        _businessLogic = businessLogic;
    }
    public ActionResult Index(int try_businessLogic)
    {
        // How do we replicate the logic in the big if-statement in above "override ActionResult Index()" method?

        var model = _businessLogic.FetchPopulatedModel;

        return View("~/Views/EmailCampaign/EmailArticle.cshtml", model);
    }
}

Это то, что у меня есть в моем классе модульного тестирования:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void Test_ArticleController_With_SitecoreItem()
    {
        //Arrange
        var businessLogicFake = new Mock<IArticleBusiness>();

        var model = new ArticleViewModel()
        {
            ArticleType = "Newsletter",
            ShowDownloadButton = true
        };

        businessLogicFake.Setup(x => x.FetchPopulatedModel).Returns(model);
        //How do I also mock the Sitecore.Context.Item and send it into the constructor, if that's the right approach?

        ArticleController controllerUnderTest = new ArticleController(businessLogicFake.Object);

        //Act
        var result = controllerUnderTest.Index(3) as ViewResult;

        //Assert
        Assert.IsNotNull(result);
        Assert.IsNotNull(result.Model);
    }
}

По сути, я хочу смоделировать Sitecore.Context.Item, который имеет значение «LinkField» (упомянутое выше как «SitecoreFieldIds.WTW_REDIRECT_TO»), каким-то образом отправить его в контроллер и выполнить ту же точную логику, что и большой оператор if. в нашем исходном методе «общедоступного переопределения ActionResult Index()».

Какой точный код для всего этого? Спасибо!


person user3034243    schedule 15.09.2017    source источник


Ответы (2)


Вы связываете свой код/логику со статическими классами, которые затрудняют изолированное тестирование. Вы также пытаетесь издеваться над кодом, над которым у вас нет контроля.

Инкапсулируйте желаемую функциональность за абстракцией, которой вы управляете.

public interface IArticleRedirectService {
    Url CheckUrl();
}

public class ArticleRedirectionService : IArticleRedirectionService {
    public Url CheckUrl() {            
        if (Sitecore.Context.Item.Fields[SitecoreFieldIds.WTW_REDIRECT_TO] != null && 
            !string.IsNullOrEmpty(Sitecore.Context.Item.Fields[SitecoreFieldIds.WTW_REDIRECT_TO].Value)) {
            var link = (LinkField)Sitecore.Context.Item.Fields[SitecoreFieldIds.WTW_REDIRECT_TO];
            if (link != null) {
                if (link.IsInternal) {
                    return Sitecore.Links.LinkManager.GetItemUrl(link.TargetItem);
                } else {
                    return link.Url;
                }
            }
        }
        return null;
    }
}

Контроллер будет явно зависеть от службы через внедрение конструктора.

public class ArticleController : GlassController {            
    private readonly IArticleBusiness businessLogic;
    private readonly IArticleRedirectionService redirect;

    public ArticleController(IArticleBusiness businessLogic, IArticleRedirectionService redirect) {
        this.businessLogic = businessLogic;
        this.redirect = redirect;
    }

    public ActionResult Index() {
        // If a redirect has been configured for this Article, 
        // then redirect to new location.
        var url = redirect.CheckUrl();
        if(url != null) {
            return Redirect(url);
        }
        var model = businessLogic.FetchPopulatedModel;    
        return View("~/Views/EmailCampaign/EmailArticle.cshtml", model);
    }
}

Теперь у кода есть возможность имитировать зависимости изолированно для модульных тестов с Moq или любой другой платформой.

person Nkosi    schedule 18.09.2017

Я настоятельно рекомендую вам использовать для этой цели Sitecore.FakeDb, который является фреймворком модульного тестирования для Sitecore. Короче говоря, насмешка над элементом контекста будет выглядеть так:

[TestCase]
public void FooActionResultTest()
{
    // arrange
    var itemId = ID.NewID;
    using (var db = new Db
    {
        new DbItem("Some Item", itemId)
        {
            new DbField(SitecoreFieldIds.WTW_REDIRECT_TO) { Value = "{some-raw-value}" }
        }
    })
    {   
        // act  
        Sitecore.Context.Item = db.GetItem(itemId);

        // assert
        Sitecore.Context.Item[SitecoreFieldIds.WTW_REDIRECT_TO].Should().Be("{some-raw-value}");
    }
}
person Aleksey Shevchenko    schedule 18.09.2017