ASP.NET, C#, MVC

ASP.NET MVC Test

UPDATE: This article is rather old now – MVC Preview 3 is out and I suggest looking at the new testing stuff in that 🙂 http://weblogs.asp.net/scottgu/archive/2008/05/27/asp-net-mvc-preview-3-release.aspx

I’ve been playing with MVC for a couple of days now as part of my professional development at Readify – and this afternoon it was time to attack a bit of testing.

So I cracked open Scott’s post on MVC and zoomed in on the unit testing code he posted.

Now, because ASP.NET MVC is quite new (its CTP, not beta, not anything, just a preview – so its *real new*) the dev community is still poking around trying out this and that and in the case of testing, not having a real smooth ride.

In Scott’s post he uses a class called TestViewEngine, which allows him to instantiate and test the views defined in the test web app project, running asserts etc on the output. But this class doesn’t exist in the MVC stuff yet – that I can see anyway (or my Googling).

So I set out on a Google fest to try to get this test code to run… and it turns out it was just a little more difficult than just creating a TestViewEngine class. The final result make use of the excellent Rhino mocking framework and my own TestViewEngine implementation.

I got the mocking code from here: http://haacked.com/archive/2007/12/09/writing-unit-tests-for-controller-actions.aspx a great article on the initial state of MVC testing. I then combine this code with Scott’s and some of my own for the final result. You can download Rhino Mocks from here: http://www.ayende.com/projects/rhino-mocks/downloads.aspx.

There are also some great posts here http://www.persistall.com/ which may help you out a bit.

Basically the code is as on haacked for the most part:

 
 [TestMethod]
        public void AllForUser()
        {

            RouteTable.Routes.Add(new Route
            {
                Url = "Snippet/Detail/[id]",
                RouteHandler = typeof(MvcRouteHandler)
            });

            SnippetController controller = new SnippetController();

            MockRepository mocks = new MockRepository();
            IHttpContext httpContextMock = mocks.DynamicMock();
            IHttpRequest requestMock = mocks.DynamicMock();
            IHttpResponse responseMock = mocks.DynamicMock();
            SetupResult.For(httpContextMock.Request).Return(requestMock);
            SetupResult.For(httpContextMock.Response).Return(responseMock);
            SetupResult.For(requestMock.ApplicationPath).Return("/");

            responseMock.Redirect("/Snippet/Detail/1");

            RouteData routeData = new RouteData();
            routeData.Values.Add("Action", "SnippetDetail");
            routeData.Values.Add("Controller", "Snippet");

            ControllerContext contextMock = new
              ControllerContext(httpContextMock, routeData, controller);
            mocks.ReplayAll();

            controller.GetType().GetProperty("TempData").SetValue(controller, new TempDataDictionary(httpContextMock), null);

            controller.ControllerContext = contextMock;

            TestViewEngine tve = new TestViewEngine();

            controller.ViewFactory = tve;

            controller.SnippetDetail(1);

            Assert.AreEqual(typeof(Snippet), tve.View.ViewData.GetType(), "Snippet object passed to view");
            Assert.AreEqual(1, tve.View.GetViewData().SnippetId, "Correct Snippet ID Processed");
            Assert.AreEqual("SnippetDetail", tve.View.ViewName, "Correct view rendered");
        }

    }

The only thing I had to add here was the following line:

</pre>
<pre>controller.GetType().GetProperty("TempData").SetValue(controller, new TempDataDictionary(httpContextMock), null);

The problem was that the RenderView call in my ControllerAction was throwing a null argument exception because TempData on the object was null and is not allowed to be – thanks to a comment by Alexey on haacked for this!

Then I created my own little TestViewEngine class to keep in line with what Scott was doing in his post:

 

 public class TestViewEngine : IViewFactory
    {

        TestView _view;

        #region IView Members

        #endregion

        #region IViewFactory Members

        public IView CreateView(ControllerContext controllerContext, string viewName, string masterName, object viewData)
        {
            _view = new TestView(controllerContext, viewName, masterName, viewData);
            return _view;
        }

        public TestView View
        {
            get
            {
                return _view;
            }
        }

        #endregion
    }

    public class TestView : IView, IViewDataContainer
    {
        ControllerContext _controllerContext;
        string _viewName;
        string _masterName;
        object _viewData;

        public TestView(ControllerContext controllerContext, string viewName, string masterName, object viewData)
        {
            _controllerContext = controllerContext;
            _viewName = viewName;
            _masterName = masterName;
            _viewData = viewData;
        }

        public T GetViewData()
        {
            return (T)_viewData;
        }

        public string ViewName
        {
            get
            {
                return _viewName;
            }
        }

        #region IView Members

        public void RenderView(ViewContext viewContext)
        {
            //throw new NotImplementedException();
        }

        #endregion

        #region IViewDataContainer Members

        public object ViewData
        {
            get
            {
                return _viewData;
            }
        }

        #endregion
    }

With this code in place you should be able to correctly run the asserts as in Scott’s article.

From what I have read there are going to be a lot of additions in the coming versions that will assist with testing – hopefully this includes things to assist with mocking the View requests etc. For now at least we can run some testing code to get us through.

ASP.NET, C#, CodeJAK, MVC, Visual Studio

CodeJAK Designs

As part of my MVC PD this week I’m creating a real website that does real things.

This website is called CodeJAK and I hope to have it go live soon.

Here is the initial design “CodeJAK design by Alex Knight” – its cool as!

So here is the low-down:

CodeJAK is a site that allows you to store code snippets – don’t let me lose you there, it’s got some cool features (okay, it *will* have some cool features).

First and foremost my plans for CodeJAK are to create a Visual Studio plugin that manages your snippets. All snippets you store in CodeJAK are stored on-line – so when you install CodeJAK on another machine and log in, all your snippets will be synched between your machines automatically.

The next feature is that you can publish your snippets if you like, i.e. share them. Then others can search for published snippets (in VS, or on the accompanying website).

Other features include community features like tagging snippets, comments on snippets, RSS feeds (subs to a feed based on user or tags or some other search query) – you get the idea (don’t steal it! – if you think its good then leave a comment and help me out!)

P.S. If you have heard of this idea before (which I havn’t) then please let me know (let me down gently).