Aug 292011
 

You will find countless blog posts, forum posts, and Stack Overflow questions concerning the topic of unit testing a ASP.NET MVC HTML Helpers. Unit testing is an art, and I am still a novice. I played around with NUnit between 2004 and 2006. I really enjoyed practicing TDD but couldn’t make it work in my 9-5 job. Finding people that are trained TDD’ers is near impossible in .NET (or maybe just in Jacksonville). Finding people that want to learn/love TDD is just as hard. It is especially difficult to convince management to move toward TDD when they see a huge upfront cost with no perceived benefit to the business/client/users. If your shop is cranking out low-defect code already, it’s a really hard sell.

In 2008, I changed jobs and started at a company that seemed to value TDD as much as I did. A new greenfield project was starting up, and everyone was tasked with learning the tools and trade of TDD. Sadly, I was not on this project. The team spent weeks, many weeks, downloading mocking frameworks and experimenting with other unit testing frameworks. They tried Moq, Rhino Mocks, Moles, TypeMock, and others. They tried NUnit, TypeMock, and MSTest. Without a TDD expert, the team spent way too much time trying to figure out the right way to test and lost track of the project’s goal… to write code to fulfill a contract. All unit testing was soon forbidden in the interest of time and money.

In my opinion, a single bad test is still better than no tests. Since that event I have tried to avoid using other frameworks and mocking libraries, etc. Sure it makes it easier, but only if you have the time to get up to speed on it and learn to love it. My primary goal on every piece of software I write is to one day be able to walk away and never get a phone call. If I lock a development team into using version 1.2.34.5678 of Crazy Mocks, they are going to 1) hate me 2) remove the test project 3) call me. I don’t want any of that to happen. I want easy-to-read code that looks like code that is versioned with the rest of the code and uses a built-in testing framework like MSTest.

So, what am I saying here and what does it have to do with MVC and fakes? I want to unit test all my code, and testing MVC HTML helpers is hard because there are lots and lots of MVC framework stuff going on that we don’t see. Simply calling your HTML helper’s methods without building the Controller, ControllerContext, HttpContext, ViewContext, ViewDataContainer, RouteData, and ViewEngine needed to support that call will give you mixed results. If your HTML helper is simple enough, you may never need to fake the view engine and context objects. If your helper is a container of built-in System.Web.Mvc.Html helpers, you are in for a tough battle. You will find many solutions to unit test HTML helpers that use Moq or Rhino Mocks. I find that developers generally accept unit tests as being worthy of the effort. They know the benefits and the costs, and try their best. When you get into a sticky situation as with MVC helpers, many give up and the code ends up having no unit tests.

A typical error message you will see is:

System.NotImplementedException – The method or operation is not implemented.

   at System.Web.HttpContextBase.get_Items()
   at System.Web.Mvc.Html.TemplateHelpers.GetActionCache(HtmlHelper html)
   at System.Web.Mvc.Html.TemplateHelpers.ExecuteTemplate(HtmlHelper html, 
      ViewDataDictionary viewData, String templateName, DataBoundControlMode mode, 
      GetViewNamesDelegate getViewNames, GetDefaultActionsDelegate getDefaultActions)
   at System.Web.Mvc.Html.TemplateHelpers.TemplateHelper(HtmlHelper html, 
      ModelMetadata metadata, String htmlFieldName, String templateName, 
      DataBoundControlMode mode, Object additionalViewData, ExecuteTemplateDelegate executeTemplate)
   at System.Web.Mvc.Html.TemplateHelpers.TemplateHelper(HtmlHelper html, 
      ModelMetadata metadata, String htmlFieldName, String templateName, 
      DataBoundControlMode mode, Object additionalViewData)
   at System.Web.Mvc.Html.TemplateHelpers.TemplateFor[TContainer,TValue](HtmlHelper`1 html, 
      Expression`1 expression, String templateName, String htmlFieldName, 
      DataBoundControlMode mode, Object additionalViewData, TemplateHelperDelegate templateHelper)
   at System.Web.Mvc.Html.TemplateHelpers.TemplateFor[TContainer,TValue](HtmlHelper`1 html, 
      Expression`1 expression, String templateName, String htmlFieldName, DataBoundControlMode mode, 
      Object additionalViewData)
   at System.Web.Mvc.Html.DisplayExtensions.DisplayFor[TModel,TValue](HtmlHelper`1 html, 
      Expression`1 expression)
   at Utils.Web.Tests.DisplayFormRowHelperTest.DisplayFormRow_StringField()
   in C:DevMvcUnitTestingUtils.Web.TestsDisplayFormRowHelperTest.cs:line 99

The ActionCacheItems are stored as a dictionary in the HttpContext.Items. If you have successfully used Moq and NSubstitute correctly and mocked away a good part of the framework, you may still see this error because the HttpContext hasn’t been built up correctly.

What can you do? Find or write some fakes. Then create your HTML helper with a method that builds up the view engine and context objects as seen in the code below:

using System.Web.Mvc;
using System.Web.Routing;
using UnitTesting.Web.Mvc;

namespace Utils.Web.Tests
{
	public class HtmlHelperBuilder
	{
		public static HtmlHelper<TModel> GetHtmlHelper<TModel>(TModel model, bool clientValidationEnabled)
		{
			ViewEngines.Engines.Clear();
			ViewEngines.Engines.Add(new FakeViewEngine());

			var controller = new MyTestController();
			var httpContext = new FakeHttpContext();

			var viewData = new FakeViewDataContainer { ViewData = new ViewDataDictionary<TModel>(model) };

			var routeData = new RouteData();
			routeData.Values["controller"] = "home";
			routeData.Values["action"] = "index";

			ControllerContext controllerContext = new FakeControllerContext(controller);

			var viewContext = new FakeViewContext(controllerContext, "MyView", routeData);
			viewContext.HttpContext = httpContext;
			viewContext.ClientValidationEnabled = clientValidationEnabled;
			viewContext.UnobtrusiveJavaScriptEnabled = clientValidationEnabled;
			viewContext.FormContext = new FakeFormContext();

			HtmlHelper<TModel> htmlHelper = new HtmlHelper<TModel>(viewContext, viewData);
			return htmlHelper;
		}
	}
}

Then your unit tests will work, and not look so obnoxious. If you compare the code below, dear blog reader with a head on your shoulders, to the Moq solutions out there, you will notice two things. 1) This looks like a lot of code, but it’s still less than Moq 2) You, and a lot of other people, can actually read this code. Moq is great, but not for most developer’s consumption.

[TestMethod]
public void DisplayFormRow_StringField()
{
	// Arrange
	var model = new MyModel { MyString = "Test" };
	string expected = "Test";
	HtmlHelper<MyModel> html = HtmlHelperBuilder.GetHtmlHelper(model, true);

	// Act
	MvcHtmlString actual = html.DisplayFor(m => m.MyString);

	// Assert
	Assert.AreEqual(expected, actual.ToHtmlString());
}

This should be enough fake action to get someone going down the right path. Feel free to use the fakes library I have linked below. No guarantees. IWOMB

Code hard!
Test lightly!

Links:

Download library of fakes – UnitTesting.Web.Mvc.zip (8.42 KB)

Feb 012010
 

I’m not sure if what I’m doing is actually the right way to create a “user control” in ASP.NET MVC, but it’s worth sharing this tidbit either way. Instead of using a MVC View User Control to create a hidden field, a text box, two anchors, and three JavaScript functions, I chose to put it all in a HtmlHelper in which I write out the HTML and JavaScript myself. Everything worked fine except the almost magical auto-repopulating of the hidden and text fields after a post that didn’t work as expected as in a typical MVC View Page.

The situation: I have a page that needs to be called as a popup from many pages in my MVC application. The page allows single or multiple selection of “items” driven by an XML file. In the event that one day, almost always immediately, I have two or more of these “controls” on one view page, I need the two fields and the three JavaScript functions to have unique names so they don’t cross paths and cause unexpected behavior. I had an ASP.NET User Control to do this in plain old ASP.NET (POAN) since v1.1, and I can’t live without it.

The confusion: If I were to place the hidden, textbox, anchors, and JavaScript functions directly in the calling page, something magical happens after a post. If the controls had values before the post, they appear to magically retain there values after the post. It wasn’t until a colleague of mine, Sat, and I dug into Reflector for a while did we realize what was happening. Html.TextBox, Html.Hidden, and others all do something similar to auto-magically re-populate their values after the post. Since I’m writing out my fields as <input type=”hidden”/> and <input type=”text”/>, the magic doesn’t happen.

      NOTE: The magic will also not happen if you just write <input type=”text”/> on the page. It only happens if you use Html.TextBox.

The solution: I am still new to MVC and still trying to wrap my head around the “right way” to do things. Reflector showed that the HtmlHelpers all looked at the ModelState in the ViewData before rendering their HTML. They looked for their value by key (key being the control/tag name), and, if present, used that as the control/tag’s value. Bing! Maybe I should do the same thing. So just before I go to town with TagBuilder to assemble my controls/tags, I look in the ViewData’s ModelState for my value. If it is there, it must have been posted there by me (my control).

   48         UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);

   49         string textValue = null;

   50         ModelState state;

   51 

   52         if (helper.ViewData.ModelState.TryGetValue(textFieldName, out state))

   53         {

   54             textValue = state.Value.AttemptedValue;

   55         }

Works like a charm! Now my hidden, textbox, two anchors, and three JavaScript functions are bundled nicely inside of an HtmlHelper class that looks and feels like I’m using a built-in ASP.NET MVC HtmlHelper class. Most importantly, I have the pleasure of typing only this on all my consuming pages.

   40     <%= Html.MySelector(“selectedIDs”, “selectedNames”, “State”)%>

Nov 272009
 

I’ve been talking about Geneva for a long time. I got the basics down earlier in the year. I tried to come up with my own set of sample apps, but failed to get anywhere. With the official release, and renaming to Windows Identity Foundation (WIF), I have renewed inspiration.

I read Michele Leroux Bustamante’s MSDN magazine article, Claim-Based Authorization with WIF, last night. After reading the article, I was confident that I could get a claims-aware WCF service stood up with a custom STS in a matter of hours. Today I downloaded and installed WIF. I also installed the WIF SDK and all of the prerequisite hotfixes. I perused the readme files and looked through some of the samples code. Everything is layed out sensibly, the samples are commented sufficiently, and the samples include setup and cleanup batch scripts when necessary.

The samples include:

Quick Start

  1. Simple Claims Aware Web Application
  2. Simple Claims Aware Web Service
  3. Simple Web Application With Information Card SignIn
  4. Simple Web Application With Managed STS
  5. Claims Aware Web Application in a Web Farm
  6. Using Claims In IsInRole

End-to-end Scenario

  1. Authentication Assurance
  2. Federation For Web Services
  3. Federation For Web Applications
  4. Identity Delegation
  5. Web Application With Multiple SignIn Methods
  6. Federation Metadata

Extensibility

  1. Claims Aware AJAX Application
  2. Convert Claims To NT Token
  3. Customizing Request Security Token
  4. Customizing Token
  5. WSTrustChannel
  6. Claims-based Authorization

All of the samples I’ve run through so far are great. The only thing that I’m not in love with is all the XML required to wire this stuff up. Maybe some Juval-style extensions would make it less painful.

One more thing – it looks like all of the XP users will finally have to upgrade. WIF only works with Vista, Win7, and Win2008. I heard that Win2003 compatibility will arrive in December.

Download Windows Identity Foundation

Download Windows Identity Foundation SDK

Sep 212008
 

WCF never ceases to amaze me. Around every corner is another fascinating use for WCF, and much forethought on Microsoft’s part to make it look and behave great. I wanted to expose my services to my AJAX functions on my web site. I did not want to change my class library because it is used by other clients. I could just add the service classes to this web site, but why re-do when you can re-use.

If you have an existing WCF Service Library, you will need to expose it with the AspNetCompatibilityRequirementsMode.Allowed attribute on the service class to make it visible to ASP.NET clients. To avoid changing your service library in any way, the easiest thing to do is to add a new class to your web site that inherits from your service class. In this example, my existing service library uses the JeepServices namespace. Notice there is no implementation in this class. It is simply a placeholder for the real service implementation with the compatibility attribute attached.

    1 using System.ServiceModel;

    2 using System.ServiceModel.Activation;

    3 

    4 [ServiceBehavior]

    5 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

    6 public class WebHttpService : JeepServices.Service

    7 {

    8 }

Now that I have a ASP.NET compatible service, I need to expose it to the web site clients. Create a service file (.svc), and change the Service and CodeBehind attributes to point to the .svc file. The last thing you need is the Factory attribute. This notifies WCF of this service, eliminating the need for a configuration file entry for the service endpoint. In fact, you don’t even need the <system.servicemodel> in your configuration file at all. This is because it is only hosted as a web script, and cannot be called outside of the web site.

    1 <%@ ServiceHost Language=“C#” Debug=“true” Service=“WebHttpService” CodeBehind=“~/App_Code/WebHttpService.cs”

    2     Factory=“System.ServiceModel.Activation.WebScriptServiceHostFactory” %>

 

In your web page you will need a few things. First your will need a ScriptManager with a ServiceReference to the .svc file. You will then need the Javascript functions to make the call (DoJeepWork), handle the success message (OnJeepWorkSucceeded), and handle the failure message (OnJeepWorkFailed). Notice in DoJeepWork that you don’t call the service by it’s service name WebHttpService, you call it by the ServiceContract namespace and name. For this example, my interface has ServiceContract attributes Namespace = “JeepServices”, and Name = “JeepServiceContract”. Now you just wire up a ASP.NET control’s OnClientClick or an input or anchor tag’s onclick to DoJeepWork() and you are good to go.

    1 <%@ Page Language=“C#” AutoEventWireup=“true” CodeFile=“Default.aspx.cs” Inherits=“_Default” %>

    2 

    3 <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”

    4 “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

    5 <html xmlns=“http://www.w3.org/1999/xhtml”>

    6 <head runat=“server”>

    7     <title>Test page</title>

    8 

    9     <script type=“text/javascript”>

   10         function DoJeepWork() {

   11             JeepServices.JeepServiceContract.DoWork(OnJeepWorkSuccedeed, OnJeepWorkFailed);

   12         }

   13         function OnJeepWorkSuccedeed(res) {

   14             document.getElementById(“<%= this.lblMessage.ClientID %>”).innerText = res;

   15         }

   16         function OnJeepWorkFailed(error) {

   17             // Alert user to the error.   

   18             alert(error.get_message());

   19         }

   20     </script>

   21 

   22 </head>

   23 <body>

   24     <form id=“form1” runat=“server”>

   25     <div>

   26         <asp:ScriptManager runat=“server”>

   27             <Services>

   28                 <asp:ServiceReference Path=“~/Services/WebHttpService.svc” InlineScript=“false” />

   29             </Services>

   30         </asp:ScriptManager>

   31         <asp:Label ID=“lblMessage” runat=“server” Text=“No work has been done” />

   32         <a href=“javascript:void(0); DoJeepWork()”>Do Work</a>

   33     </div>

   34     </form>

   35 </body>

   36 </html>

 

Mission accomplished! Here you’ve seen how to expose an existing WCF service library without changing any code in the library itself. Adding two files allowed the service to be exposed to your AJAX clients. Best of all, there is no configuration file changes to make.

Useful Links:

Jan 212008
 

In a previous post about the AJAX Extensions, I detailed the copy commands to retrieve the DLLs from the GAC. Same thing, this time for the ASP.NET 3.5 Extensions. If you are demoing CTP material in a hosted environment, you will likely need these in your app’s bin to avoid the inevitable configuration error.


copy “C:WINDOWSassemblyGAC_MSILSystem.Web.Extensions3.6.0.0__31bf3856ad364e35” C:devMMVCAppbin
copy “C:WINDOWSassemblyGAC_MSILSystem.Web.Extensions.Design3.6.0.0__31bf3856ad364e35” C:devMMVCAppbin

Dec 192007
 

A great series of blog posts by Scott Guthrie about the ASP.NET MVC Framework coming soon as part of the ASP.NET 3.5 Extensions release.


Upon hearing the news, a few friends started questioning its intent, usefulness, and longevity. Many of us have been using or contemplating conversion to the MVP pattern, most recently using WCSF. The recent split of the MVP pattern by Fowler has caused many believers to question their faith. While many are still “proving” MVP, MVC has been around for nearly 30 years. Some believe that MVP and MVC can co-exist. Here is a comparison of MVP and MVC that concludes by painting an optimistic picture of MVP and MVC contributing to each other.


ASP.NET MVC appears to be the answer to my unit testing, REST, and code separation prayers. Thank you ScottGu and team!


Check it out!

Feb 102007
 

Many hosting companies, like mine, won’t have the AJAX Extensions installed in the GAC for quite a while, if ever.   No worries, you can just put the DLLs in your Bin folder.   Since MS hasn’t graced us with the DLLs, so you’ll have to get them out of your GAC.  I haven’t found a way to copy DLLs using Windows Explorer, but command-line never fails.


copy “C:WINDOWSassemblyGAC_MSILSystem.Web.Extensions1.0.61025.0__31bf3856ad364e35” C:devMyAppBin
copy “C:WINDOWSassemblyGAC_MSILSystem.Web.Extensions.Design1.0.61025.0__31bf3856ad364e35” C:devMyAppBin