This is how ASP.NET MVC controller actions should be unit tested

I’m upgrading my partywithpalermo.com website for the MVP Summit party, and I’m retrofitting my tests to use the March CTP of the MVC Framework.  I have the following action that I need to unit test:

public class MainController : ControllerBase
{
    private readonly IAttendeesRepository _repository;
 
    public MainController(IAttendeesRepository repository, IViewEngine viewEngine)
    {
        _repository = repository;
        ViewEngine = viewEngine;
    }
 
 
    public void Register(string name, string website, string comment)
    {
        var attendee = new Attendee(name, website, comment);
        _repository.SaveNewAttendee(attendee);
        RenderView("confirm", attendee);
    }
}

Note the explicit dependencies on IAttendeeRepository and IViewEngine.  That means that I’ll be interacting with those two dependencies in this controller.  Here is my unit test (this passes, by the way):

[Test]
public void ShouldSaveAttendee()
{
    var repository = new FakeRepository();
 
    var mockViewEngine = new MockViewEngine();
    var controller = new MainController(repository, mockViewEngine);
    controller.ControllerContext = new ControllerContext(new RequestContext(new HttpContextStub(), new RouteData()), controller);
    controller.Register("Jeffrey.',", "http://www.jeffreypalermo.com?=&@%20", "this comment!?,'.");
 
    Attendee attendee = repository.SavedAttendee;
    Assert.That(attendee.Name, Is.EqualTo("Jeffrey.',"));
    Assert.That(attendee.Website, Is.EqualTo("http://www.jeffreypalermo.com?=&@%20"));
    Assert.That(attendee.Comment, Is.EqualTo("this comment!?,'."));
 
    Assert.That(mockViewEngine.ActualViewContext.ViewName, Is.EqualTo("confirm"));
    Assert.That(mockViewEngine.ActualViewContext.ViewData, Is.EqualTo(attendee));
}
 
private class HttpContextStub : HttpContextBase
{
 
}

This is a pretty straightforward unit test except for the line before calling the Register() method.  I have to use setter-injection to set a stubbed ControllerContext.  If I don’t, the Register() method will bomb with an exception when the ViewContext is created, and the code tries to get an HttpContextBase off of the ControllerContext.  It’ll throw a NullReferenceException.  My code doesn’t really care about the ControllerContext or what is in it, but because of how the code is structured, I must use setter injection to break this dependency.  Note that testability is next up on the list of things to do for the MVC Framework team.

Preview2 (March CTP) was all about getting the routing engine out into its own assembly so that it can be used separate from MVC controllers.  Also, the MVC Framework is “binnable”.  You can xcopy deploy it.  There is plenty of time to fix these things, and the team is working on it.  You can also be sure that I’ll keep raising the issue because I’ve been test-driving code for three years, and it’s instinct now to see what is easy and frictionless and separate it from code that is harder to test than it should be.  Overall, pretty good for a CTP.

The following is what I would like to write.  The following is how I would like my test to look.  Notice just the absence of the ControllerContext line.

[TestFixture]
public class MainControllerTester
{
    [Test]
    public void ShouldSaveAttendee()
    {
        var repository = new FakeRepository();
 
        var mockViewEngine = new MockViewEngine();
        var controller = new MainController(repository, mockViewEngine);
        controller.Register("Jeffrey", "http://www.jeffreypalermo.com", "this comment");
 
        Attendee attendee = repository.SavedAttendee;
        Assert.That(attendee.Name, Is.EqualTo("Jeffrey"));
        Assert.That(attendee.Website, Is.EqualTo("http://www.jeffreypalermo.com"));
        Assert.That(attendee.Comment, Is.EqualTo("this comment"));
 
        Assert.That(mockViewEngine.ActualViewContext.ViewName, Is.EqualTo("confirm"));
        Assert.That(mockViewEngine.ActualViewContext.ViewData, Is.EqualTo(attendee));
    }
 
    private class MockViewEngine : IViewEngine
    {
        public ViewContext ActualViewContext;
 
        public void RenderView(ViewContext viewContext)
        {
            ActualViewContext = viewContext;
        }
    }
 
    private class FakeRepository : IAttendeesRepository
    {
        public Attendee SavedAttendee;
 
        public IEnumerable<Attendee> GetAttendees()
        {
            throw new NotImplementedException();
        }
 
        public void SaveNewAttendee(Attendee attendee)
        {
            SavedAttendee = attendee;
        }
    }
}

Note that my unit test is concerned with explicit dependencies and doesn’t know or care that I’m making use of a PROTECTED method named “RenderView” inside my action.  That detail doesn’t matter because the interaction with the IViewEngine is what is important. 

I also understand that I could use the Legacy code pattern of “Extract and Override Call” (Feathers, p348).  Microsoft has already provided the extracted method, RenderView.  I can override the call to break the dependency, but that pattern is meant to be used as a dependency-breaking technique with legacy code.  If you haven’t read Michael Feathers’ book, Working Effectively with Legacy Code, you should order it right now.  It talks about all kinds of dependency-breaking techniques in order to write unit tests on existing code.  My goal in providing feedback to the MVC Framework team (and I provide feedback, believe me) is to have this framework be something that I would want to use.  There is plenty of time to make the necessary changes, and the team is working hard.

I revised this post a bit after chatting on the phone with Scott Guthrie this evening.  We’ll see some testability improvements in the next drop, and the team is aiming to make drops around every 6 weeks or so.

Note:  I’m playing around with using “var” for locals.  Not sure if I like it yet.  We’ll see.  No need to comment on the “vars”.  Comment on controllers, actions, and unit tests.

New drop of ASP.NET MVC Framework now available

My RSS feed:  http://feeds.feedburner.com/jeffreypalermo

You can download it here.  It’s public and announced at the MIX conference.  A new release of the ASP.NET MVC Framework.  The license allows  you go “go live” and use it in production.  You’ll have to uninstall the December CTP first.  The new install will only drop the assemblies in C:Program FilesMicrosoft ASP.NET MVC Preview 2Assemblies, and then you’ll have to copy them over to your application and drop them in the “bin” folder.

We’ll be upgrading MvcContrib and CodeCampServer to work with the new bits soon.

From looking at the bits, here are some notable changes:

  • Constructor of Route now takes more information (GOOD)
  • There are IDictionary arguments for many things, and that is a GOOD thing.  Before anonymous types were required.
    I like the way Monorail handles this by allowing querystring syntax like: RedirectToAction(“foo”, new string[] {“orderid=” + theOrder.Id});
  • [ControllerAction] is gone.  Public methods are actions by default. (GOOD)
  • RenderView() methods are still protected and only 1 of them is virtual (would like IViewEngine to be used more explicitly)
  • 6 (SIX) members of the Controller class are still marked internal (I’d like to be able to extend them)
  • RouteValueDictionary is just a wrapper for the anonymous type.  (I think we can work towards a better API)
  • Seven properties don’t have setters, such as IPrincipal User {get;} (I’d like to see setters)
  • I still see SEALED classes.  In an extensible framework, sealed is your enemy (I’ll be making not of the sealed classes I’d like to extend)
  • ViewContext is not usable in unit test scenario because of its dependency on HttpContextBase. (It can be refactored to help testability)
    Still can’t mock out the IViewEngine’s RenderView method and have it work in a unit test.  I’m told the team is tackling this next.
  • ComponentController is a welcome addition to enable nested controllers, but, sadly, non of the members are virtual.
  • Lots of view helpers (GOOD)
  • Routing is a separate assembly (GOOD)
  • System.Web.Mvc.dll can be deployed in the bin instead of the GAC (GOOD)
  • We can go live with this drop (and it appears to me to be stable enough for small applications)
  • The team is committed to roughtly 6 weeks in between drops (GOOD).  Release early.  Release often.

Two more people to pay attention to

RSS feed:  http://feeds.feedburner.com/jeffreypalermo

Jimmy Bogard:  Jimmy works for me at Headspring Systems as a senior consultant.  He brings quite a bit of agile knowledge to the table, and he is a leader in the Austin community.  He presented at the last Austin Code Camp, and he is currently leading a book club on Eric Evans’ Domain-Driven Design book.  He is a committer on the NBehave project, and I could go on.  Check out his latest post about letting the customer drive the iteration demo.

Eric Hexter:  Eric Hexter is an ASP.NET expert and really understands how to make a web application scale.  Eric and I co-founded the MvcContrib project together, and he is responsible for most of the configuration management including the build and the automated publishing of releases.  Eric also helps lead the Austin .Net User Group as one of the three directors, and he is responsible for the execution of the 2007 Austin Code Camp.

Should I close the cli_dev (altnetconf) Yahoo group?

RSS feed:  http://feeds.feedburner.com/jeffreypalermo

I have set up a poll on the cli_dev yahoo group here:  http://tech.groups.yahoo.com/group/cli_dev/surveys?id=2005679

I feel an obligation to follow up on this issue since I am the group owner on record with Yahoo.

Here is a message I sent to the group just in case you no longer track the list formerly known as “altnetconf”:

=================================================

Guys,
I am the yahoo owner of this list. I originally grabbed it so that
we’d have a place for discussion after the October AltNetConf. I
deferred to David Laribee and Scott Bellware when it came to leading
and fostering the Alt.Net community.
It appears now that there are other lists where the Alt.Net dialog is
occurring.
I have created a poll:
http://tech.groups.yahoo.com/group/cli_dev/surveys?id=2005679
Please let me know opinions. It seems that spammers are auto-creating
accounts, and the relevant discussion here is very sparse.
If the poll results in a “yes” answer, I’ll take down this list.

==================================================

I believe that it’s possible to shut down the list while keeping the history searchable, but that’s another decision to be made:  Keep the history or trash it?

Opening up Monorail’s SmartDispatchController for easy unit testing

Monorail’s SmartDispatchController is fantastic.  One small hurdle is testability right out of the box.  For instance, if I have a controller action:

public void Foo()
{
    Session["somekey"] = new object();
}
 
If I want to unit test this action, I need to get at the Session IDictionary.  It’s protected, so I can’t get at it in a unit test to ensure the object was added to session.
 
Here is a quick way around this problem:
 
Make your own controller base class that derives from SmartDispatchController.  Then add the following method to the base class:
 
 
public new virtual IDictionary Session
{
    get { return base.Session; }
}

Now, for testing you can get at the Session property, but at runtime, there is no change.

Going and speaking at Tech Ed 2008 Developers: session catalog available

This year, Tech Ed has been divided into two conferences.  June 3-6, 2008 is for developers, and June 10-13 is for IT Pros.  I’ll be speaking at the developers conference and giving three sessions (rss feed:  http://feeds.feedburner.com/jeffreypalermo):

  1. Black Belt Software Configuration Management: Source Control, Builds, Database Migration, Testing, and Deployment (Development Practices track):  As part of the new “development practices” track, this session will focus on all the ways to make the development process speed up.  So many of these practices are emphasizes in extreme programming, and I’ll be sharing the combination I and my company use to make software projects manage themselves.  All tools demonstrated with only require the VS 2008 Pro sku.  You can have an awesome SCM process without purchasing VSTS.
  2. Data Access Layer: Architectural Concerns for Object/Relational Mappers (O/R-M) (Architecture track):  All examples will use NHibernate, but that’s not the focus of this session.  This session will speak to the architecture of a system that uses and O/R mapper.  One of the common, early mistakes of folks starting to use O/R mappers is mixing data access and domain logic.  Having used NHibernate for over 2.5 years, I’ve worked out how to build an application that uses an ORM without the whole application knowing about it.  Lessons in this session can be applied to LINQ to SQL and EF as well.
  3. Model View Controller (MVC) Architecture Pattern: Considerations for the New ASP.NET MVC Framework (Architecture track):  There will be plenty of talk at Tech Ed about the new ASP.NET MVC Framework, but this is the only talk scheduled that purely addresses architectural concerns.  I think it’s fairly easy to pick up Monorail or ASP.NET MVC and start using it, but it’s not easy to immediately understand how to set up the presentation layer when leveraging the MVC pattern, regardless of framework used.  This session will cover project references, early and late binding, testing architecture, and builds.

All of the techniques I demonstrated in my sessions will use VS2008Pro (not VSTS).  My goal is to maximize the audience that can benefit from the session, not just those whose companies have purchases team system.  With that, I’ll be showing lots of other tools, some free, some commercial.

I’ll also be planning a Party with Palermo for Tech Ed that will be held on June 2nd at 7pm, so stay tuned for those details.

This will be my first time speaking at Tech Ed, and I’m a bit surprised that they accepted all my session proposals given that I’m not one to openly evangelize the latest and greatest from Microsoft.  Each of my sessions will be focused on how to get work done, not tell you how great products are.  The ASP.NET MVC Framework is new, and lots of people are in love with it. I’m an early adopter, but I also am compelled to address challenges of the framework and how to get around them.  It’s not all gravy, so prepare to get your hands dirty if you come to my sessions.

I have an Agile/XP focus in software development as well, so that will come through regardless of the topic on which I’m speaking.  If you are planning on going to DevTeach, Toronto, I’ll be giving the Blackbelt SCM and ASP.NET MVC talk at that conference as well.

Announcing ASP.NET MVC in Action (from Manning)

ASP.NET MVC in Action is a book from Manning that covers the newly-released ASP.NET MVC Framework. Jeffrey Palermo, Ben Scheirman, and Jimmy Bogard teamed up to write this advanced volume.  It is not a beginner book.  It is not a professional book.  It is an advanced book for ASP.NET professionals.  These three Alt.Net authors share best practices, patterns, and lots of opinions on how to use the new framework. 

UPDATE:  9/21/2009 – The book is published now.  You can find more information here.

[tags: aspnetmvc mvc asp.net palermo] 

Pain-Driven Development: uncovering the motivation

To subscribe to my feed, add the following Url:  http://feeds.feedburner.com/jeffreypalermo.

Kevin Hurwitz and I talk at length about pain-driven development.  For those who don’t know, Kevin is my lead architect and .Net Practice Manager at Headspring Systems, and we’ve worked together since mid-2006.  We both have the same mindset about PDD.  If we are motivated to put time into changing some aspect of the software, it has to be related to one or more areas of pain.  For instance, if the build is taking too long, we might look for ways to speed up the build.  If a particular interface has too many responsibilities, it may annoy us enough to break it up. 

Now, we do have a low threshold for pain.  We expect our software configuration management to be frictionless and go completely smoothly, including automatic database migrations; therefore, pain points that pop up are obvious, and we tend to kill the pain by fixing the source very quickly.

The point of this post is to think of pain reduction as ROI.  In business, we might spend money on software for some expected return on investment.  While working with the software, we also have returns on investment, but in the form of productivity.  Pain kills productivity.  Over time, we might learn to live with pain through parts of the process by taking a constant does of <insert painkiller here/>, but that only hides the pain.  It doesn’t cure it. 

Pain-driven development is a mindset where developers react intensely to pain and solve it so that it goes away once and for all.  PDD practitioners don’t just cover up the pain.  We eradicate it.  PDD leads to a completely frictionless software process that is a joy to experience.

Note:  I’m not advocating another XDD acronym, but I wanted to share our mindset.

First make it right. Then make it fast.

To subscribe to my feed, add the following Url:  http://feeds.feedburner.com/jeffreypalermo.

I spent the first part of my career on the side of the fence where I want to make it fast first.  In fact, I wanted to keep it fast all the way through.

I’m talking about software.  Performance optimization.  Speed. 

There was a time when every time I concatenated a string I weighed the theoretical pros and cons of using the plus operator vs StringBuilder vs. string.Format().  I even measured differences.  I’ve now come to the point where I realize that PDD (pain-driven development) is the real decision-maker.  It’s no longer worth my time to think about what concatenation method might be faster.  Compared to a single database call, the speed different is irrelevant.

Point:  A well-designed application can be tuned at any conceivable place after it’s working correctly.  An application that has “performance optimizations” coded into it from the start has less flexibility for the inevitable bottleneck that surfaces unexpectedly.

Point:  I will not guess at what part of my code will be a bottleneck unless I have past experience in the exact same scenario.  Rather, I will lead the team to create correct software that _appears_ to be fast enough throughout the project.  At an agreed-upon time we’ll profile (with the appropriate tools – yes, I mean _measure_) the application and find any bottlenecks that are not acceptable for production.  Then, with real data in hand, we’ll tune exactly the right parts of the application.  We will not guess before measuring.  After measuring, guessing is not necessary.

Point:  First make it right.  Then make it fast.

In general, out of process calls are performance bottlenecks, not in-process logic.  Either way, measuring tells the truth, not guessing.