Asp.net MVC Testing Imbalance

Writing

Update: I have put up a new post here that shows how I have simplified some of my testing. If you like it please kick it so that others will see the follow up post!

Here is my "Update" action on a project that I am working on:

public void Update(int id)
{
    _viewData.User = _userRepository.GetUser(id);
    BindingHelperExtensions
        .UpdateFrom(_viewData.User, Request.Form);
    _userRepository.SubmitChanges();
 
    RedirectToAction("List");
}

And here is my test for it (using Moq, I wanted to try it out. This uses the Moq MvcMockHelpers from Scott Hanselman's post):

public void UserControllerUpdateTest()
{
    RouteTable.Routes.Add(
        new Route("[controller]/[action]", new MvcRouteHandler()));
 
    var mockUserRepository = new Mock<IUserRepository>();
 
    var user = new User()
    {
        Id = 10,
        UserName = "TestUser",
        EmailAddress = "test@test.com",
        ModifiedOn = DateTime.Now,
        CreatedOn = DateTime.Now
    };
 
    mockUserRepository.Expect(ur => ur.GetUser(10))
        .Returns(user);
    mockUserRepository.Expect(ur => ur.SubmitChanges());
 
    var fakeController = 
        new UserControllerForTesting(mockUserRepository.Object);
    FakeViewEngine fakeView = new FakeViewEngine();
    fakeController.ViewEngine = fakeView;
 
    var context = new MockHttpContextContainer();
 
    var formData = new NameValueCollection();
 
    var userNameFormData = "TestUser2";
    var emailAddressFormData = "test2@test.com";
 
    formData.Add("User.UserName", userNameFormData);
    formData.Add("User.EmailAddress", emailAddressFormData);
 
    context.Request.Expect(r => r.Form)
        .Returns(formData);
 
    var routeData = new RouteData();
    routeData.Values.Add("Action", "Add");
    routeData.Values.Add("Controller", "Home");
    fakeController
        .SetFakeControllerContext(context.Context.Object, 
        routeData);    
 
    fakeController.Update(10);
 
    mockUserRepository.VerifyAll();
 
    Assert.AreEqual(fakeController.RedirectedAction, "List");
    var viewData = fakeController.TestingViewData;
    Assert.IsNotNull(viewData);
    Assert.IsNotNull(viewData.User);
    Assert.AreEqual(viewData.User.UserName, 
        userNameFormData);
    Assert.AreEqual(viewData.User.EmailAddress, 
        emailAddressFormData);
}

And that doesn't even include any of my helper objects or methods. Anyone see a problem with this? I am going to have to put some serious effort into streamlining this. I know that there is quite a bit I can abstract out across different controllers and actions so we will see how this goes. Feel free to provide feedback if you like. So far though I am really having fun digging into this, I can't believe I waited this long!

Loved the article? Hated it? Didn’t even read it?

We’d love to hear from you.

Reach Out

Comments (9)

  1. Hi Justin,
    thanks for the feedback on Moq.

    The complexities you mention are more related to the wiring up of the various pieces of the MVC than Moq, but the point is taken. We’re thinking about having additional deliverables on top of Moq that will make this easier, i.e. Moq.MVC, which could include something like a MockContext which already has everything hooked-up and is ready to use.

    Feel free to join the project, spike an implementation and set it to us. We’re very open to contributions 🙂

    Thanks!

  2. @Daniel Honestly I wasn’t really trying to critique or offer any feedback on Moq, I guess I didn’t make that very apparent. 🙂 I was more saying that the wiring up of everything to test very simple scenarios in MVC can take quite a bit of effort. Like you said, it wouldn’t be any less code in any of the other mocking frameworks, and in some it would have been more.
    Since this was my first venture into Moq, I want to say that I really did like the use of lambdas in describing the expectations. I find Moq very easy to work with, and will probably continue to experiment with it.
    Also, If you look at the code above, I have created a MockHttpContextContainer that allows me to expose all the different parts of the HttpContext as Mock<T> so that I can set my needed expectations for things like form data. I was going to add more helper methods and whatnot on there, is that something similar to what you were considering building?

  3. Justin,

    I’ve learned that writing maintainable tests requires just as much dedication and creativity as writing maintainable functional code. I found the book "xUnit Test Patterns: Refactoring Test Code" to be very helpful.

    Also, the "Object Mother" pattern could be used in your example for thinning the code in the actual test case.

    Chris

  4. @Chris I agree completely, people will only test as long as it is easy for them to test. In fact, this applies to most everything. It either has to be easy or they have to have no other option. And I will look at the "Object Mother" pattern and how it might help me with my testing. Thanks.

  5. It’s interesting, the blog engine platform seems very variable in form. My design skills are not so good as my C coding though, I would be interested in seeing what additional skins you can get for it. Nice blog btw, best wishes for it and keep up the posts. 🙂 Kind regards, Peter sims.

  6. I just realized that there’s a feature in Moq that will make mock httpcontext unnecessary: fluent mocks and Mock.Get. Let me explain:

    when you create a mock, you can specify that it returns mocks instead of nulls for unset expectations:

    var context = new Mock<HttpContextBase> { DefaultValue = DefaultValue.Mock };

    You can then get the mock that is returned by using Mock.Get<T>:

    // set additional expectations on the response Mock.Get(context.Object.Response).Setup(r => r.ContentType = "application/xml")

Leave a comment

Leave a Reply

Your email address will not be published.

More Insights

View All