Unit Testing a Very Simple ViewResult in MVC

Technorati Tags: ,,

I have a view that really only provides content, is there a quick way to test that it exists? What’s the Assert?

There’s really not much to unit test if you have a page that has only HTML in a MVC (Model-View-Controller) project. And to some extent, if you had a test that called it, if someone removed that View from the controller, the test would no longer compile. However, I don’t like having tests without an Assert. So here are two views called in slightly different ways. One is more testable than the other:

The two views…
  1. public ViewResult IndexNamed()
  2. {
  3.     return View("IndexNamed");
  4. }
  5.  
  6. public ViewResult Index()
  7. {
  8.     return View();
  9. }

 

The Passing Test
  1. [TestCategory("Build"), TestCategory("Unit"), TestMethod]
  2. public void IndexNamed()
  3. {
  4.     // Arrange
  5.     string expected = "IndexNamed";
  6.     string actual;
  7.     var controller = new AdminController();
  8.  
  9.     // Act
  10.     var result = controller.IndexNamed() as ViewResult;
  11.  
  12.     // Assert
  13.     actual = result.ViewName;
  14.     Assert.AreEqual(expected, actual);
  15. }

 

The FAILING Test
  1. [TestCategory("Build"), TestCategory("Unit"), TestMethod]
  2. public void Index_Not_Named()
  3. {
  4.     // Arrange
  5.     string expected = "Index";
  6.     string actual;
  7.     var controller = new AdminController();
  8.  
  9.     // Act
  10.     var result = controller.Index() as ViewResult;
  11.  
  12.     // Assert
  13.     actual = result.ViewName;
  14.     Assert.AreEqual(expected, actual);
  15. }

The second test fails with this error message:

Assert.AreEqual failed. Expected:<Index>. Actual:<>.

I’m not sure why the ViewName is not automatically set when you user the View() method, but it’s not. You have to call it with the name so now I’m in the habit of always calling it with that. Perhaps if I have some other need to implement my own controller base class I’ll change that behavior.

Unit Testing CodeSmith Insight

As anyone that knows me knows, I use test driven development. So when I put together a Feedback page in my MVC (Model-View-Controller) site I started with my test. What should the test be?

Let’s start with the User Story : As a site user (anonymous or logged in) I want to submit feedback to the webmaster so that I can get bugs fixed or get features I want.

So the HelpController has a view called Feedback that lets a user input some feedback and submit it using the CodeSmith Insight product.

I finally settled on a test like this…

The Test
  1. [TestCategory("Build"), TestCategory("Unit"), TestMethod]
  2. public void FeedbackReceived()
  3. {
  4.     // Arrange
  5.     string expected = "FeedbackReceived";
  6.     string actual;
  7.     var detail = new CaseReportDetail() { Description = "Test", Title = "Test" };
  8.     var controller = CreateControllerAsNonAdmin(false);
  9.  
  10.     // Act
  11.     var result = controller.Feedback(detail) as ViewResult;
  12.  
  13.     // Assert
  14.     Assert.IsTrue(controller.IsCaseSubmited); // Was the case submitted?
  15.     actual = result.ViewName;
  16.     Assert.AreEqual(expected, actual); // Did it forward to the right view?
  17. }

That seems simple enough, but there’s obviously some hidden details here… What’s a CaseReportDetail? What’s the controller? And what’s the property IsCaseSubmitted?

CaseReportDetail

The CaseReportDetail is merely a model that I leveraged from the samples that came with the CodeSmith Insight. Here is my class:

CaseReportDetail
  1. public class CaseReportDetail
  2. {
  3.     public string Email { get; set; }
  4.     public string Title { get; set; }
  5.     public string Description { get; set; }
  6. }

Ok… that’s pretty simple, now what about the controller?

The Controller

The controller is a bit trickier. That code leverages the Moq (pronounced Mock You) framework. Here is what that code looks like:

Getting HelpController via Moq
  1. private HelpController CreateControllerAsNonAdmin(bool submitCaseFails)
  2. {
  3.     var mock = new Mock<ControllerContext>();
  4.     mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns("test@nowhere.com");
  5.     mock.Setup(p => p.HttpContext.User.IsInRole("Admin")).Returns(false);
  6.     mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
  7.  
  8.     var controller = new HelpController(new MockInsightManager(submitCaseFails));
  9.     controller.ControllerContext = mock.Object;
  10.  
  11.     return controller;
  12. }

But as we drill into that it begs a question, “What is the MockInsightManager?” As with many classes, the InsightManager does not have an interface and has some “non-overrideable” members that make Mocking hard. One trick I use when I encounter that is a wrapper with an interface. So let me show all these items, the IInsightManagerBase, InsightManagerWrapper, and MockInsightManager so you can get a sense for the overall layout:

IInsightManagerBase
  1. public interface IInsightManagerBase
  2. {
  3.     bool IsCaseSubmitted { get; }
  4.     CaseReport CreateCase();
  5.     void SubmitCase(CaseReport caseReport);
  6. }

InsightManagerWrapper
  1. public class InsightManagerWrapper : IInsightManagerBase
  2. {
  3.     public InsightManagerWrapper()
  4.     {
  5.         Manager = InsightManager.Current;
  6.     }
  7.     
  8.     private InsightManager Manager { get; set; }
  9.  
  10.     public bool IsCaseSubmitted
  11.     {
  12.         get;
  13.         private set;
  14.     }
  15.  
  16.     public Exception FailureException { get; set; }
  17.  
  18.     public CaseReport CreateCase()
  19.     {
  20.         return Manager.CreateCase();
  21.     }
  22.  
  23.     public void SubmitCase(CaseReport caseReport)
  24.     {
  25.         try
  26.         {
  27.             Manager.SubmitCase(caseReport);
  28.             IsCaseSubmitted = true;
  29.         }
  30.         catch (Exception ex)
  31.         {
  32.             FailureException = ex;
  33.             IsCaseSubmitted = false;
  34.         }
  35.     }

MockInsightManager
  1. public class MockInsightManager : IInsightManagerBase
  2. {
  3.     public MockInsightManager(bool submitCaseFails)
  4.     {
  5.         SubmitCaseFails = submitCaseFails;
  6.     }
  7.     public bool SubmitCaseFails { get; set; }
  8.     public bool IsCaseSubmitted { get; set; }
  9.  
  10.     public CaseReport CreateCase()
  11.     {
  12.         return InsightManager.Current.CreateCase();
  13.     }
  14.  
  15.     public void SubmitCase(CaseReport caseReport)
  16.     {
  17.         if (!SubmitCaseFails)
  18.         {
  19.             IsCaseSubmitted = true;
  20.         }
  21.     }
  22. }

So now we have a lot of the backup code, what does the actual view code in the controller look like? I’ll first show what it would look like if you just used the insight manager for those that want to see what it would look like when using Insight out of the box:

Feedback method out-of-box
  1. [AcceptVerbs(HttpVerbs.Post)]
  2. public ActionResult Feedback(CaseReportDetail detail)
  3. {
  4.     try
  5.     {
  6.         var report = InsightManager.Current.CreateCase();
  7.         report.EmailAddress = detail.Email;
  8.         report.Title = detail.Title;
  9.         report.Description = detail.Description;
  10.         report.CaseType = CaseType.Inquiry;
  11.         InsightManager.Current.SubmitCase(report);
  12.         return View("FeedbackReceived", detail);
  13.     }
  14.     catch
  15.     {
  16.         ModelState.AddModelError("Feedback", "There was a problem submitting the feedback. You may try to press submit again to try again. We apologize for the inconvenience.");
  17.         return View();
  18.     }
  19. }

With no real indirection above, now let’s look at the controller after I modified it for easier unit testing.

Here’s the top of the controller class where I have a constructor:

HelpController constructor
  1. public class HelpController : Controller
  2. {
  3.     private IInsightManagerBase _insightManager { get; set; }
  4.     public HelpController()
  5.         : this(new InsightManagerWrapper())
  6.     {
  7.     }
  8.     public HelpController(IInsightManagerBase insightManager)
  9.     {
  10.         _insightManager = insightManager;
  11.     }

And here is the modified View method based on this new constructor:

Feedback
  1. [AcceptVerbs(HttpVerbs.Post)]
  2. public ActionResult Feedback(CaseReportDetail detail)
  3. {
  4.     var report = _insightManager.CreateCase();
  5.     report.EmailAddress = detail.Email;
  6.     report.Title = detail.Title;
  7.     report.Description = detail.Description;
  8.     report.CaseType = CaseType.Inquiry;
  9.     _insightManager.SubmitCase(report);
  10.     if (_insightManager.IsCaseSubmitted)
  11.     {
  12.         return View("FeedbackReceived", detail);
  13.     }
  14.     else
  15.     {
  16.         ModelState.AddModelError("Feedback", "There was a problem submitting the feedback. You may try to press submit again to try again. We apologize for the inconvenience.");
  17.         return View();
  18.     }
  19. }

The Negative Case

So now the original test works… but what happens if something goes wrong when submitting the case?

I added a new test that looks like this:

Feedback_Not_Received
  1. [TestCategory("Build"), TestCategory("Unit"), TestMethod]
  2. public void Feedback_Not_Received()
  3. {
  4.     // Arrange
  5.     string expected = "Feedback"; // Did not work so we stayed on the Feedback view showing the error.
  6.     string actual;
  7.     var detail = new CaseReportDetail() { Description = "Test", Title = "Test" };
  8.     var controller = CreateControllerAsNonAdmin(true);
  9.  
  10.     // Act
  11.     var result = controller.Feedback(detail) as ViewResult;
  12.  
  13.     // Assert
  14.     Assert.IsFalse(result.ViewData.ModelState.IsValid); // Was the model invalid?
  15.     actual = result.ViewName;
  16.     Assert.AreEqual(expected, actual); // Did it forward to the right view?
  17. }

Then when I was done, I had 100% code coverage for the Feedback method!

I hope this helps others unit test the CodeSmith Insight…

CodeSmith Insight Overview

Technorati Tags:

CodeSmith Insight is a very nice tool that’s currently in Beta targeted and collecting feedback from your users… to provide you with “Insight” to your customers needs.

I’ve been beta testing this for about 1 week now and have been very impressed. I’ll put some of my findings here as well as some tips for using CodeSmith Insight.

Where is CodeSmith Going?

Flying near the Microsoft flame is always a challenge. As I saw Microsoft putting energy toward their T4 templates and features such as the Entity Framework I wondered what CodeSmith Tools would do. I’ve used their CodeSmith Generator for years now. It’s a very powerful template based code generation tool. As I’ve watched the Entity Framework as well as LINQ to SQL develop over the past several years I wondered what CodeSmith Tools would do to reinvent themselves. I recently found the answer when I got a Beta invitation for Insight.

Of course CodeSmith faces the same challenge many companies face when the company name and the product name are the same. If you look on their website (at the time of this posting) for their products you see that CodeSmith Professional 5.2 and CodeSmith Standard 5.2 are available. Of course that’s the code generator.

They too saw the decreased need for code generation tools and have been looking for new ways to have value to the Microsoft development community. And they’ve certainly found a way.

What Is CodeSmith Insight?

CodeSmith Insight (Insight) is a tool for gaining insights to your customers (or in my case… users) needs. CodeSmith is leveraging the latest cloud computing technology from Microsoft to produce… A product? No… and SDK (Software Development Kit)?  No… a Service? No… all three!

CodeSmith has leveraged the latest technologies to provide the ability for your software to easily collect information from your users. With very little effort – less than 15 minutes – I was able to have my website collect crash reports for me! It doesn’t track user information and thus doesn’t violate any privacy yet allows me to see crashes and manage them with rich information so that I can correct the issue.

That’s really the tip of the iceberg, but a very important tip.

Right now I use Insight for a Microsoft ASP.NET MVC 2 application I’m developing, but I plan to use it for other applications in the future too.

I’ll place some tips here as I work with CodeSmith Insight over the coming weeks.

Way to go CodeSmith!

Unit Test Authorize Attribute

Technorati Tags: ,,,

Story: I want to write a unit test to ensure that the Authorize attribute is applied to a controller ActionResult or ViewResult so that security is tested.

Here is the test:

Unit Test
  1. [TestMethod]
  2. public void PreviousSiteUserConversion_Authorization_Attributes_Have_Been_Applied()
  3. {
  4.     // Arrange
  5.     MethodInfo varietalMethod = typeof(AdminController).GetMethod("PreviousSiteUserConversion", new Type[] { });
  6.  
  7.     // Act
  8.     var attributes = varietalMethod.GetCustomAttributes(typeof(AuthorizeAttribute), true);
  9.  
  10.     // Assert
  11.     Assert.IsNotNull(attributes);
  12.     Assert.AreEqual(1, attributes.Length);
  13.     var authAttribute = (AuthorizeAttribute)attributes[0];
  14.     string[] roles = authAttribute.Roles.Split(new char[] { ',' });
  15.     Assert.IsTrue(roles.Contains(RoleNames.Admin));
  16. }

And here is the code:

Working Code
  1. public class AdminController : Controller
  2. {
  3.     [Authorize(Roles = RoleNames.Admin)]
  4.     public ActionResult PreviousSiteUserConversion()
  5.     {
  6.         return View();
  7.     }
  8.  
  9. }

The unit test does pass.

There are also some great posts out there about using strongly typed role and user names. My needs are pretty simple… I just need a single role that I strongly type.

dbboon and proxy.php broke my GoDaddy site?

Technorati Tags:

Headline: I deleted the folder and now my site works fine.

I’ve seen other posts on this, but the word I got from GoDaddy today is that this file is required for some features involving “widgets”. Since I don’t use any of those features, this file was breaking my site. I honestly don’t remember if I’d tried to look at any other applications to install or not, so I might have triggered something to put this on the site.

Bottom line though… I didn’t need it… and actually having it would break my site. It would have been nice if the first phone call had discovered that issue. The second phone call after finding this was quite helpful.

Impressed with CrystalTech for .NET 4.0

Technorati Tags: ,,

I’ve used CrystalTech for years. They seemed to be falling behind a bit when I started to work with the MVC (Model-View-Controller) pattern. That works MUCH better with IIS 7 than with IIS 6, but CrystalTech only had IIS 6 servers.

That’s all changed!

I now have no excuses for not upgrading my sites (which will take a long time)! I just rolled to a new plan – I’m on Microsoft Windows Server 2008 R2 – With IIS 7, and .NET 4.0! And I had a site setup (not deployed, but at least a site I could work with) 10 days after the release of .NET 4 and Visual Studio 2010.

Way to go CrystalTech!

My SSD Rocks

Technorati Tags: ,,,

Headline: My new Intel 160 GB X25M SSD (Solid State Drive) is really fast! 200+ MB/sec read and 90+ MB/sec write!

Background

I sort of smiled when I opened up my new drive this week to see a little sticker that said, “My SSD Rocks!”. You see, I had a SSD before… The original one that came with my Dell, but I was starting to run short on space. At this time I’m using several Virtual PCs to test software so the 128 GB I had was pretty tight… So I decided to get a “larger” drive.

While searching for a larger drive I still wanted performance. There are definitely larger drives out on the market, but I really wanted a balance between performance and size. After a fair bit of research I settled on the Intel X25M drive.

When it got here I could really tell a difference… But how much of that is because I didn’t have all the software loaded on? Well, now I have all the same software on the new drive that I had on the old one. Was it really faster?

The Machines

These are the last three configurations I’ve had and that I compare:

Dell Latitude D820 with 7200 rpm Drive – This was the notebook I purchased in 2006… A dual core, but I replaced the drive with a 7200 rpm drive. For years the drive has been the limiting factor.

Dell Precision M4400 with original 128 GB SSD – Knowing that the drive is the slowest piece in the system I swore that I would not buy another notebook (the D820 still is a great notebook) until the drive performance improved. Well, when I saw that I could get a quad core with a SSD last year, I did and got in 9 Jan 2009. It came with a 128 GB SSD.

Dell Precision M4400 with Intel 160GB SSD – So this is the exact same Dell M4400, but with the new Intel X25M SSD.

The Results

For the comparison I used PassMark PerformanceTest 7.0 as well as the Windows 7 Experience Index.

If you look at the properties of your computer (right click on your computer and then select Properties) your page should look something like this:

image

When I click on the “Windows Experience Index” link, here are my new results:

image

When I had the same machine, but the original SSD the Primary Hard Drive number was 5.9 compared the 7.7 that Windows now shows. I don’t know how they compute these, but if there is any relation to the real performance, that’s 30% improvement.

I that that was really cool… but then I ran the PassMark tests… WOW!

image

Look at the Random Seek + RW numbers… My traditional hard drive looks like a “dot” rather than a “bar”. But what really impressed me was the raw read/write times! My reads are 5.5 times faster and my writes are twice as fast. (Note: The test results for the original SSD 6 months after I had it so there may be some slow down in that drive.)

So needless to say, I’m a pretty happy camper and smile when I see the little “My SSD Rocks!” sticker on my desk.

Goodbye Groove

Technorati Tags: ,,,

I started using Lotus Notes in 1992… But then IBM didn’t seem to be able to market their way out of a paper bag after buying Lotus. Ray then ran off to write the next version and started Groove. I was SO disappointed when he made the same silly mistake of limiting the size of a Groove workspace (it was a database in Lotus) to a mere 2 GB. Even more disappointing was the reduction in usability.

So what’s happened? Why no more Groove?

I started using OneNote since their very first release. While I didn’t have a table PC I still found the concepts useful. By 2006, just one year after Microsoft acquired Groove Networks I was starting to wonder which would win – OneNote or Groove.

What did Groove have?

Groove still had the key collaboration concepts.

  • You could see new (to you) items.
  • There was security (strong encryption and basic certificates).
  • Instant Messaging (IM) was built in so that you could see who (for your workspace) was online.
  • It would replicate (sync) information through many tight firewall situations.

What did OneNote have?

OneNote was an excellent analogy to long standing “Notebooks” (The paper variety that would come in wide rule or college rule)

  • Taking notes was easy.
  • I can have a shared notebook on pretty much any file server. This allows me to “Not backup, but instead, use redundant storage as a backup strategy” (an idea borrowed from the Facebook team)
  • Searching was VERY easy.
  • I can share a notebook with others.

Key Differences

Sharing with Others – With OneNote all I need is a drive… not some fancy Groove server (which is clearly overkill for information I wish to share with my wife.)

And the latest issue… I want to change hard drives. I have my OneNote notebooks on my Windows Home Server (WHS). Changing drives is a piece of cake. Contrast that with Groove. I have some workspaces that at this stage I am the only  am a participant. I can find no way to get the information off my old drive and onto my new on since they cannot both be online at the same time!

What really highlighted this for me was that I had one workspace called “Software We Own”. That workspace had key installation files, license keys, and other information for reloading software. With now way to get that information from my old drive to my new drive I could not install my new software.

I now find that I have NO access to my “Software We Own” workspace. If it had been a OneNote notebook, I would just need to open that shared notebook.

Today

So today I copied all the information from my Groove workspace and pasted it into a OneNote notebook that I share with my wife via our Windows Home Server (WHS).

Closing

A sad day in my mind. The collaborative capabilities of both Lotus Notes and Groove was insightful. I’ve always described the key power of these technologies as “Syncing your brain with the information on the computer”. Knowing what I’ve seen and not seen was vital. The ability to see usage patterns, i.e., who’s seen what information and when, were valuable to information providers. I have some wonderful stories about the power of the collaboration of both Lotus Notes and Groove…

But in the end, they can’t seem to meet the simple needs. So for my needs at the moment… On to OneNote. My truly collaborative needs are met by other technologies such as Team Foundation Server (TFS) and various social networks.

Thank you OneNote.

MVC – The Start

This shows my initial setup of an MVC 2 RC 2 application. Using Visual Studio 2010 RC I selected File –> New –> Project… then under Visual C#, Web, I selected the ASP.NET MVC 2 Web Application.

image

Then when I click OK I get the following dialog. Since I’m a very strong believer in Unit Tests I of course do create an associated Unit Test project.

image

The first thing I did then was to run the unit tests… there were 18. I like to know the code coverage too… So I have to create some test settings and turn on the code coverage. Right clicking on the solution I add a new item:

image

Out of habit I call it Local.testsettings because I usually end up with a Build.testsettings that is set up differently.

image Under Data and Diagnostics I turn on code coverage and configure it. This is a bit tricky for the moment. For this to really work you need to add an assembly and select the actual .dll. Here are those screen shots for my project:

I don’t select any of the items that show up by default:

image

but instead I select the Add Assembly…

image

I navigated to the bin folder of my MVC application and selected that dll. Go ahead and select OK on the next message:

image

Click OK on the next dialog:

image Then hit Apply and Close on the Test Settings window. Now when I run all my tests again I can open the code coverage window:

image

And when I do, I see that I have about 50% code coverage.

image  

There are several other things that I do before I consider the project initially setup…

I turn on Code Analysis and select the Microsoft All Rules rule set.

image

After that I run the code analysis (Analyze –>  Run Code Analysis on KarlZMvcGallery) to see there are 14 warnings. I’ll breeze through getting rid of these but I will highlight some important items. The first is to create a Code Analysis Dictionary. I’m not sure why it is not as easy as merely adding a new item to the project, but you have to create a new file and type in it yourself. I created one from someone’s post so long ago that I don’t recall where that post is, but I copy that file from project to project. Here is the file I have to get rid of the “CA1704 : Microsoft Naming: Correct the spelling of …” warnings:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <Dictionary>
  3.   <Words>
  4.     <Unrecognized>
  5.       <Word>cb</Word>
  6.     </Unrecognized>
  7.     <Recognized>
  8.       <Word>Mvc</Word>
  9.     </Recognized>
  10.     <Deprecated>
  11.       <Term PreferredAlternate="EnterpriseServices">ComPlus</Term>
  12.     </Deprecated>
  13.   </Words>
  14.   <Acronyms>
  15.     <CasingExceptions>
  16.       <Acronym>GEDCOM</Acronym>
  17.     </CasingExceptions>
  18.   </Acronyms>
  19. </Dictionary>

After you add CodeAnalysisDictionary.xml to your project, you need to change the Build Action for the file to Code Analysis Dictionary:

image

I don’t know why the AssemblyInfo.cs file does not already have these two lines, but I need to add them each time:

  1. using System;
  2. [assembly: CLSCompliant(true)]

With a bit more work and some suppression of messages such as “Consider consolidating the namespace” in your project suppression file you have a project that has 0 Errors, 0 Warnings, and 0 Messages!

Now I feel like I’ve got a project initially setup.

MVC Overview

Technorati Tags:

What’s different about Microsoft’s ASP.NET MVC (Model-View-Controller)?

There are some very good books about MVC (such as Steven Sanderson’s book), but here are the features I enjoy the most:

Routing – The routing model in MVC makes you really think about what you want your URLs to look like. In EVERY instance my application ended up much simpler than I originally thought.

Controller – The controller does most of the binding work for you so you don’t have to constantly set the values of controls in your code behind.

*-Driven Development – My top two would be Test Driven Development (TDD) and Model Driven Development. MVC reinforces both.

Extension Methods – The HtmlHelper class is quite powerful and one can easily extend it if features are missing. This lets you type something like:

  1. <%= Html.DropDownListFor(model => model.Category, ViewData["Categories"] as SelectList) %>

I feel fortunate that I worked with web applications for many years. That made the move to WPF (Windows Presentation Foundation) much easier than someone moving from WinForms. However, the Binding model was a significant change from WebForms. The notion that I didn’t have to constantly write MyTextBox.Text = model.Property in my code behind was a relief. I finally appreciated Binding and then started to work with MVC where the Controller class provides the same capability.

I still do very little client side code because it is typically so hard to test, but I see slow improvements.

Not for Every Problem

While MVC is very powerful, it does not need to be the only tool you use on the web. However, I find that for data driven applications the model is much more natural. Having just finished a full application and benefiting from the power of MVC it will be one of the most used tools for me. But have no fear… You can fairly easily mix and match traditional controls within MVC… I’ll eventually have some examples of that.