This post has been migrated from www.experimentsincode.com, we apologise if some of the images or content is missing

Posts in this series: So this is the second post on how to unit test Sitecore, in this post I look at how to restructure some simple code so that we can test the code via the NUnit GUI. For this example we will have a very simple sub-layout that contains a comment form, this form takes a comment, username and email address from the user and then adds this to the Sitecore database. So the code for out sub-layout looks like this:
public partial class AddComment : System.Web.UI.Page
    {

        protected override void OnInit(EventArgs e)
        {
            submit.Click += new EventHandler(submit_Click);
            base.OnInit(e);
        }

        void submit_Click(object sender, EventArgs e)
        {
            //a few presets for a comment
            Guid commentTemplate = new Guid("{73888B25-314C-4F93-8F7C-48E916C054C9}");
            DateTime commentDate = DateTime.Now;
            string commentName = string.Format("comment-{0}", commentDate.ToString("dd-MM-yyyy-hh-mm-ss"));

            //create a new comment
            Item parent = Sitecore.Context.Database.GetItem("/sitecore/content/home/news/interestingarticle");
            Item newComment = parent.Add(commentName, new TemplateID(new ID(commentTemplate)));

            //populate the comment

            newComment.Editing.BeginEdit();

            newComment["username"] = username.Text;
            newComment["comment"] = comment.Text;
            newComment["email"] = comment.Text;
            newComment["date"] = DateUtil.ToIsoDate(commentDate);

            newComment.Editing.EndEdit();

        }
    }
So we can turn this into something testable using the MVP pattern. We can create a presenter and a view interface:
  public class CommentPresenter
    {
        ICommentView _view;
        Database _database;

        public CommentPresenter(ICommentView view, Database database)
        {
            _view = view;
            _database = database;
        }

        public void SubmitComment()
        {
            try
            {
                //a few presets for a comment
                Guid commentTemplate = new Guid("{73888B25-314C-4F93-8F7C-48E916C054C9}");
                DateTime commentDate = DateTime.Now;
                string commentName = string.Format("comment-{0}", commentDate.ToString("dd-MM-yyyy-hh-mm-ss"));

                //create a new comment
                Item parent = _database.GetItem(_view.ParentItem);

                using (new SecurityDisabler())
                {

                    Item newComment = parent.Add(commentName, new TemplateID(new ID(commentTemplate)));

                    //populate the comment

                    newComment.Editing.BeginEdit();

                    newComment["username"] = _view.Username;
                    newComment["comment"] = _view.Comment;
                    newComment["email"] = _view.Email;
                    newComment["date"] = DateUtil.ToIsoDate(commentDate);

                    newComment.Editing.EndEdit();
                }

                _view.Action(CommentViewAction.CommentAdded);
            }
            catch (Exception ex)
            {
                _view.ErrorMessage = ex.Message;
                _view.Action(CommentViewAction.ErrorMessage);
            }
        }

    }
    public enum CommentViewAction
    {
        CommentAdded,
        ErrorMessage
    }

    public interface ICommentView
    {
        string ParentItem { get;  }
        string Comment { get;  }
        string Username { get; }
        string Email { get;  }
        string ErrorMessage { set; }
        void Action(CommentViewAction action);
    }
With these classes we can then modify the sub-layout to use the presenter. We change the sub-layout to inherit from the comment interface, then on the page load we instantiate the presenter passing in the sub-layout and the Sitecore context database into the presenter. The sub-layout then becomes very thin with all the logic in the presenter.
public partial class AddComment : System.Web.UI.Page, ICommentView
    {
        CommentPresenter _presenter;

        protected override void OnInit(EventArgs e)
        {
            _presenter = new CommentPresenter(this, Sitecore.Context.Database);

            submit.Click += new EventHandler(submit_Click);
            base.OnInit(e);
        }

        void submit_Click(object sender, EventArgs e)
        {
            _presenter.SubmitComment();
        }

        #region ICommentView Members

        public string ParentItem
        {
            get { return "/sitecore/content/home/news/interestingarticle"; }
        }

        public string Comment
        {
            get { return comment.Text; }
        }

        public string Username
        {
            get { return username.Text; }
        }

        public string Email
        {
            get { return email.Text; }
        }

        public string ErrorMessage
        {
            get;
            set;
        }

        public void Action(CommentViewAction action)
        {
            switch (action)
            {
                    //Just quickly output the results
                case CommentViewAction.CommentAdded:
                    Response.Write("Comment has been added");
                    break;
                case CommentViewAction.ErrorMessage:
                    Response.Write(string.Format("An error as occured, {0}", ErrorMessage));
                    break;

            }
        }

        #endregion
    }
Now we have a separation between presentation and logic we can test the logic in the presenter works how we think it should. Within the test project we can create the following test fixture:
    [TestFixture]
    public class CommentPresenterFixture
    {
        [Test]
        public void SubmitComment_CreatesAComment()
        {
            string comment = "testComment";
            string email = "testEmail";
            string username = "testUsername";
            string parent = "/sitecore/content/home/news/interestingarticle";

            Database db = Sitecore.Configuration.Factory.GetDatabase("master");

            Mock view = new Mock();
            view.Setup(x => x.Comment).Returns(comment);
            view.Setup(x => x.Email).Returns(email);
            view.Setup(x => x.Username).Returns(username);
            view.Setup(x => x.ParentItem).Returns(parent);

            CommentPresenter presenter = new CommentPresenter(view.Object, db);
            presenter.SubmitComment();

            view.Verify(x => x.Action(CommentViewAction.CommentAdded));

            view.VerifyAll();

            Item parentItem = db.GetItem(parent);
            //not the best line I admit, but there are limits to what we can do
            Item newChild = parentItem.Children.Cast().Last();

            Assert.AreEqual(comment, newChild["comment"]);
            Assert.AreEqual(email, newChild["email"]);
            Assert.AreEqual(username, newChild["username"]);

        }
    }
So in this class we mock the view interface so that it returns my predefined values; for the mocking I have used Moq. I also instantiate a Sitecore database using the Sitecore factory.  You can then see that the rest of the testing is quite straight forward. Running the test should hopefully return lots of green lights. The only line that I am not particularly happy with is the following:
Item newChild = parentItem.Children.Cast().Last();
Unfortunately at the moment I can't think of a more sensible way to get the item that was created by Sitecore. So we now have system that give us red/green testing of modifications to Sitecore, we can now properly test our interactions with Sitecore. While doing this sort of testing you might want to setup a second instance of Sitecore  that your tests run against so that your development instance does not get polluted by test items. You could also add clean up code to the end of each tests, in the example above we could delete the comment item after we have finished the tests.
comments powered by Disqus