Unit Testing MVC projects in Kentico 12

Richard Brantley asked on March 13, 2020 00:01

I'm trying to figure out how to unit test two types of controllers.

Section controllers, which descend from SectionController

Recent Answers


Arjan van Hugten answered on March 13, 2020 10:01

You can look in to the dancing goat MVC solution. There are some tests that you can use as an example. For example widget controllers are being tested in the dancing goat site. I think that section controllers (if you use properties for the section) need the same kind of way of testing.

2 votesVote for this answer Mark as a Correct answer

Peter Mogilnitski answered on March 13, 2020 12:03

1 votesVote for this answer Mark as a Correct answer

Richard Brantley answered on March 18, 2020 15:57 (last edited on March 18, 2020 16:06)

I had a lot more to my question but my entry seems to keep getting cut off.

The section controller retrieves an object of type T by calling GetProperties(), which is implemented in the SectionController base class.

The page template controllers make a call to a method called GetPage(), which is implemented in the ancestor PageTemplateController class, that returns a TreeNode.

In both cases I have code that is creating and populating a model and generating some kind View or Partial View, and I want to test that the correct view is called and the model is correctly populated. But...I can't figure out how to mock or setup the test for GetProperties or GetPage, now can I figure out how to generate a TreeNode object. I've looked through the examples attached to the Kentico.Libraries.Tests, and haven't found anything that looks applicable.

This is a section controller sample. I got around the problem by creating a public property for the DefaultSectionProperties and use that if it's populated and calls GetProperties if not, but that feels both hacky and ugly. I would rather have some way in my test to setup what the call to GetProperties in the base class returns.

public class DefaultSectionController : SectionController<DefaultSectionProperties>
{
    public DefaultSectionProperties SectionProperties;

    public ActionResult Index()
    {
        var properties = RetrieveProperties();
        var model = new DefaultSectionViewModel()
        {
            BackgroundColor = properties.BackgroundColor,
            Columns = properties.Columns
        };
        return PartialView("Sections/_DefaultSection", model);
    }

    private DefaultSectionProperties RetrieveProperties()
    {
        return SectionProperties != null ? SectionProperties : GetProperties();
    }
}

Anything I should be looking at?

0 votesVote for this answer Mark as a Correct answer

Arjan van Hugten answered on March 18, 2020 17:20 (last edited on March 18, 2020 17:30)

Hi,

The GetProperties call is executed in the Kentico 'ComponentPropertiesRetriever' with the 'Retrieve' function. This is done by the 'SectionController' class.

The same for the 'PageTemplateController' that uses the 'Retrieve' function on the 'CurrentPageRetriever'.

So both these methods need to be mocked. That is done like this:

For the section:

private readonly IComponentPropertiesRetriever<CustomSectionProperties> propertiesRetriever = Substitute.For<IComponentPropertiesRetriever<CustomSectionProperties>>();

propertiesRetriever.Retrieve().Returns(new CustomSectionProperties { Title = TITLE });

For the page template:

private readonly ICurrentPageRetriever currentPageRetriever = Substitute.For<ICurrentPageRetriever>();
private readonly IPageBuilderFeature pageBuilder = Substitute.For<IPageBuilderFeature>();

currentPageRetriever.Retrieve(pageBuilder).Returns(article);

The tests that I created in the DancingGoat are like this:

Section test:

[TestFixture]
class CustomSectionControllerTests : UnitTests
{
    private const string PARTIAL_VIEW_NAME = "Sections/_CustomSection";
    private const string TITLE = "Test Title";

    private readonly IComponentPropertiesRetriever<CustomSectionProperties> propertiesRetriever = Substitute.For<IComponentPropertiesRetriever<CustomSectionProperties>>();

    [Test]
    public void Index_ReturnsCorrectModel()
    {
        propertiesRetriever.Retrieve().Returns(new CustomSectionProperties { Title = TITLE });

        var controller = new CustomSectionController(propertiesRetriever, Substitute.For<ICurrentPageRetriever>());
        controller.ControllerContext = ControllerContextMock.GetControllerContext(controller);

        controller.WithCallTo(c => c.Index())
            .ShouldRenderPartialView(PARTIAL_VIEW_NAME)
            .WithModel<CustomSectionViewModel>(m => m.Title == TITLE);
    }
}

Template test:

[TestFixture]
class TemplateControllerTests : UnitTests
{
    private const string VIEW_NAME = "PageTemplates/_Article";

    private readonly ICurrentPageRetriever currentPageRetriever = Substitute.For<ICurrentPageRetriever>();
    private readonly IPageBuilderFeature pageBuilder = Substitute.For<IPageBuilderFeature>();

    [Test]
    public void Index_ReturnsCorrectModel()
    {
        Fake().DocumentType<Article>(Article.CLASS_NAME);
        Article article = new Article()
        {
            ArticleTitle = "Title 1",
            ArticleText = "Text 1"
        };

        // Dont use the 'ControllerContextMock.GetControllerContext(controller)' because the 'IPageBuilderFeature' needs to be equal to the mocked 'currentPageRetriever.Retrieve(pageBuilder)'
        var httpContext = Substitute.For<HttpContextBase>();
        pageBuilder.EditMode.Returns(true);
        httpContext.Kentico().SetFeature(pageBuilder);

        currentPageRetriever.Retrieve(pageBuilder).Returns(article);

        var controller = new ArticlePageTemplateController(currentPageRetriever);
        controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);

        controller.WithCallTo(c => c.Index())
            .ShouldRenderView(VIEW_NAME)
            .WithModel<ArticleViewModel>(m => m.Title == "Title 1" && m.Text == "Text 1");
    }
}

Note that you need to create constructors in your section/template controllers for the unit tests.

/// <summary>
/// Creates an instance of <see cref="ArticlePageTemplateController"/> class.
/// </summary>
/// <param name="currentPageRetriever">Retriever for current page where is the widget used.</param>
/// <remarks>Use this constructor for tests to handle dependencies.</remarks>
public ArticlePageTemplateController(ICurrentPageRetriever currentPageRetriever) : base(currentPageRetriever)
{
}
0 votesVote for this answer Mark as a Correct answer

Arjan van Hugten answered on March 20, 2020 09:56

Hi,

Did you already look into my solution? Does this fit your needs?

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.