Unit testing Sitecore Webforms? It’s possible with the MVP pattern.

As I have described in my previous post about unit testing with Sitecore MVC, unit testing with Sitecore MVC is possible. To make that easy/possible you have to abstract some of the Sitecore classes away into a wrapper. You quickly get used to the fact that the common sitecore classes are abstracted away and you are programming against a wrapper.

One of the perks of MVC as opposed to Webforms development is that is is easy to unit test. This is partially due to the fact that the controls of a Webforms page or control are directly accessed in the code behind. To make a Webforms project more unit testable, you can implement the MVP pattern.

The Model-View-Presenter pattern (or MVP)

I am not stating that is is easy to unit test a Webforms project, but when choosing the right pattern for the job it can be done. The Model-View-Presenter pattern (MVP) is my pattern of choice for this matter. It does a nice job of separating the logic from the presentation. When using the MVP pattern I use the WebformsMVP framework which can be easily installed using Nuget.

Example

I have created a simple example in which I want to have a Usercontrol that contains an image and an image description. If no image is available I want the image to be hidden.

The Model

The model we are using to provide the data to the view looks like this:

public class MediaImageModel
{
    public string Image { get; set; }

    public bool ShowImage
    {
        get { return !string.IsNullOrEmpty(Image); }
    }

    public string ImageDescription { get; set; }
}

The Image property contains the image url.

The Presenter

The presenter manipulates the model and handles the user interaction if necessary which is not the case here.

public class MediaImagePresenter : Presenter<IView<MediaImageModel>>
{
    private readonly ISitecoreContext sitecoreContext;

    public MediaImagePresenter(IView<MediaImageModel> view, ISitecoreContext context)
        : base(view)
    {
        sitecoreContext = context;
        this.View.Load += Load;
    }

    public void Load(object sender, EventArgs e)
    {
        IMediaImageItem model = sitecoreContext.CurrentItem as IMediaImageItem;

        this.View.Model.Image = model.Image.Url;
        this.View.Model.ImageDescription = model.ImageDescription.RenderedValue;
    }
}

The presenter take a view that uses a model of type MediaImageModel. The constructor takes a view which is injected by the WebformsMvp framework. It also takes a context of type ISitecoreContext which is injected using Unity.

this.View.Load

is a method that is fired during the Load event. Since I am still using Synthesis to access the items through generated interface representations of that template, the current item is cast to the IMediaImageItem interface.

The View – codebehind

public partial class MediaImage : MvpUserControl<MediaImageModel>
{}

This control inherits from MvpUserControl which uses a MediaImageModel. Through conventions the WebformsMvp framework links the MediaImagePresenter to the MediaImage control.

The View – markup

<asp:Image runat="server" ID="Image" Visible="<%#Model.ShowImage %>" ImageUrl="<%#Model.Image %>"/>
<asp:Literal runat="server" ID="ImageDescription" Text="<%#Model.ImageDescription %>"/>

The load event in the presenter is fired. After that the public property Model is filled and available in the markup.
All done…except for the unit testing part.

Unit testing

Since we have now separated the logic from the presentation we can determine if the model is filled according to our specifications. to test this I have created three unit tests.

public partial class MediaImage : MvpUserControl<MediaImageModel>
[TestClass()]
public class MediaImagePresenterTests
{
    private Mock<IMediaImageItem> CreateNewMockMediaImageItem(string imageDescription = "", string imageUrl = "")
    {
        var mediaImageItem = new Mock<IMediaImageItem>();

        mediaImageItem.SetupGet(x => x.Image).Returns(new TestImageField(imageUrl));
        mediaImageItem.SetupGet(x => x.ImageDescription).Returns(new TestTextField(imageDescription));
        return mediaImageItem;
    }

    private Mock<IMediaImageView> CreateNewMockMediaImageView()
    {
        return new Mock<IMediaImageView>();
    }

    [TestMethod()]
    public void Load_IMediaImageItemWithEmptyImageUrl_ImageEmptyAndInvisible()
    {
        // Arrange
        Mock<IMediaImageItem> mediaImageItem = CreateNewMockMediaImageItem(imageDescription: "", imageUrl: "");
        Mock<IMediaImageView> view = CreateNewMockMediaImageView();
        view.SetupGet(x => x.Model).Returns(new MediaImageModel());
        SitecoreContext sitecoreContext = new SitecoreContext() { CurrentItem = mediaImageItem.Object };

        // Act
        MediaImagePresenter presenter = new MediaImagePresenter(view.Object, sitecoreContext);
        presenter.Load(null, null);

        // Assert
        Assert.AreEqual("", presenter.View.Model.Image);
        Assert.AreEqual(false, presenter.View.Model.ShowImage);
    }

    [TestMethod()]
    public void Load_IMediaImageItemWithFilledImageUrl_ImageFilledAndVisible()
    {
        // Arrange
        Mock<IMediaImageItem> mediaImageItem = CreateNewMockMediaImageItem(imageDescription: "", imageUrl: "imageurl.jpg");
        Mock<IMediaImageView> view = CreateNewMockMediaImageView();
        view.SetupGet(x => x.Model).Returns(new MediaImageModel());
        SitecoreContext sitecoreContext = new SitecoreContext() { CurrentItem = mediaImageItem.Object };

        // Act
        MediaImagePresenter presenter = new MediaImagePresenter(view.Object, sitecoreContext);
        presenter.Load(null, null);

        // Assert
        Assert.AreEqual("imageurl.jpg", presenter.View.Model.Image);
        Assert.AreEqual(true, presenter.View.Model.ShowImage);
    }

    [TestMethod()]
    public void Load_IMediaImageItemWithImageDescription_ImageDescriptionFilled()
    {
        // Arrange
        Mock<IMediaImageItem> mediaImageItem = CreateNewMockMediaImageItem(imageDescription: "TestImageDescription", imageUrl: "");
        Mock<IMediaImageView> view = CreateNewMockMediaImageView();
        view.SetupGet(x => x.Model).Returns(new MediaImageModel());
        SitecoreContext sitecoreContext = new SitecoreContext() { CurrentItem = mediaImageItem.Object };

        // Act
        MediaImagePresenter presenter = new MediaImagePresenter(view.Object, sitecoreContext);
        presenter.Load(null, null);

        // Assert
        Assert.AreEqual("TestImageDescription", presenter.View.Model.ImageDescription);
    }
}

And there you have it: A unit tested usercontrol that uses the MVP pattern. For simple sites I must say that this approach can make simple things complex. You have to write more code to accomplish the same.
I think that when you want to unit test a Webforms project, you should look into the MVP pattern. When building a new project, I would go with MVC.

Leave a Reply

Your email address will not be published. Required fields are marked *