CodeThinked

codethinked (kōdthĭngked) adj. To be consumed by or obsessed with code.

Asp.net MVC Testing Imbalance

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!

Comments

trackback

Trackback from DotNetKicks.com

Asp.net MVC Testing Imbalance

DotNetKicks.com

March 20. 2008 16:18

Maarten Balliauw

When you start testing MVC, you'll probably run into other issues... I posted two of them on my blog: blog.maartenballiauw.be/.../...issues-Q-and-A.aspx

It is indeed strange that 6 lines of controller code mean numerous lines of testing code. But hey, it's only a CTP currently and things may improve. At least I hope too Smile

Maarten Balliauw

March 21. 2008 06:53

Belgium
Justin Etheredge

@Maarten Yeah, I'm glad that they are taking their time and developing the MVC framework in a more open process. I think it will really allow them to get it right.

Justin Etheredge

March 21. 2008 11:29

United States
Daniel Cazzulino

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 Smile

Thanks!

Daniel Cazzulino

March 22. 2008 17:01

Argentina
Justin Etheredge

@Daniel Honestly I wasn't really trying to critique or offer any feedback on Moq, I guess I didn't make that very apparent. Smile 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?

Justin Etheredge

March 22. 2008 18:30

United States
trackback

Trackback from CodeThinked

Simplified Asp.net MVC Controller Testing with Moq

CodeThinked

March 23. 2008 04:01

Chris

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

Chris

March 25. 2008 15:46

United States
Justin Etheredge

@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.

Justin Etheredge

March 25. 2008 15:56

United States
pingback

Pingback from code-inside.de

Wöchentliche Rundablage: WPF, Silverlight 2, ASP.NET MVC, .NET 3.5… | Code-Inside Blog

code-inside.de

March 31. 2008 18:02

Franchise News

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. Smile  Kind regards,  Peter sims.

Franchise News

June 11. 2009 08:23

United States
Daniel Cazzulino

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")

Daniel Cazzulino

June 11. 2009 12:10

Argentina
Justin Etheredge

@Daniel Very cool, thanks for letting me know about this! I am going to put up a blog post about that.

Justin Etheredge

June 11. 2009 12:31

United States
Comments are closed