>

Writing integration tests with tools like WatiN usually involves a lot of "magic strings" which makes the tests very fragile and refactoring unfriendly. This leads to tests that are hard to maintain and as the project moves on they end up being commented out or not being run at all. There are two ways to produce integration test for web applications with tools like WatiN , Selenium etc.

1. Record the tests with a recorder

2. Write the tests using code.

I prefer #2 because this lets me to produce my tests before the functionality is done. (Integration tests TDD-style) The technique described in this post apply to method #2.

So how can we produce more robust tests using Asp.Net MVC and WatiN?

Lets explore how we can combine strongly typed views and forms in Asp.Net MVC with some “Lambda-fu” to help us write refactoring friendly WatiN tests.

The benefits we get is when we refactor controller actions, view data and user input our integration test will automatically be updated to reflect the changes. Another benefit is that you can write you integration tests in true TDD-style and let Resharper generate the code for you. ( Remember, Alt +  Enter is your best friend!)

Enough talk, time for some code!

Binding to controller actions

The idea here is to use lambda expressions to bind our tests to controller actions and then infer the Url from the class and method names of the controller. The following test demonstrates this

[Test]
public void Can_execute_action_with_arguments()
{
    var productsController = new ControllerProxy<ProductsController>(browser);
 
    productsController.ExecuteAction(x => x.Edit(123));
 
    browser.AssertWasCalled(b => b.GoTo(http://localhost/Products/Edit/123));
}

As you can see we create a “ControllerProxy” for the Asp.Net MVC controller called ProductsController and then executes the “Edit” action passing the parameter value “123”. The browser object that is passed to the proxy is the WatiN -  IBrowser interface that abstracts the different supported browsers (IE and Firefox). This makes is easy to run the integration tests for both browsers.

The code in controller proxy looks like this:

public ControllerProxy<CONTROLLERTYPE> ExecuteAction(Expression<Func<CONTROLLERTYPE, Object>> expression)
{
    var methodCall = (MethodCallExpression)expression.Body;
    var url = BuildUrlFromControllerAction(methodCall.Method.Name);
 
    foreach (var argument in methodCall.Arguments)
        url += "/" + argument;
 
    browser.GoTo(url);
 
    return this;
}

As you can see we generate the correct url from the controller name and the specified action. Then we append the action parameters and finally tell WatiN to browse the url.

I wont bore you with the rest of the code but if you are interested it can be found here.

Validating view data

Ok, now that that we can execute controller actions in a strongly typed manner wouldn't it be nice if we could validate the resulting view (if of course the action results in a rendered view). This can be easily achieved if you use Asp:net MVC support for strongly typed views (if you don't, you really should! Ayende shares my position in this matter :)

This test demonstrates how we can assert that the rendered view contains expected values.

[Test, ExpectedException(typeof(AssertionException))]
public void Enable_validation_of_view_data()
{
    var productsController = new ControllerProxy<ProductsController>(browser);
 
    var mockTextBox = MockRepository.GenerateMock<IElement>();
 
    browser.Stub(b => b.Element("Name")).Return(mockTextBox);
 
    mockTextBox.Stub(textbox => textbox.Text).Return("Not the name we expected");
 
    productsController.AssertViewData<CreateViewModel>(x=>x.Name == "Expected name");
}

Here the call to “AssertViewData” binds to the ViewModel called “CreateViewModel” and then makes sure that the property value of “Name” is “Expected Name” (which in this test it’s not and a AssertionException is raised). This solution has a potential flaw in that is expects you to generate the html input control with the id “Name”. The way to solve this is to use a view engine or view helpers that allows you to bind your html controls directly the view model property. My personal favourite is FluentHtml which allows you to use he following syntax:

<%= this.TextBox( x=>x.Name) %>
 
instead of:
 
<%= Html.TextBox("Name") %>

 

User input and submitting forms

By utilizing Asp.Net MVC support for strongly typed user input we can add support for a “magic string free” way of inputting and submitting data using WatiN. Firsts lets look at a controller action that accepts strongly typed data

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(CreateProductForm userInput)
{

In this example “CreateProductForm” contains a “Name” and a “Price” property that the user inputs. When the form is submitted Asp.Net MVC’s model binders automatically binds the submitted data to the above properties. Let see how we can handle this scenario using our controller proxy (I talked about this in my previous post)

[Test]
public void Can_submit_formdata()
{
    var productsController = new ControllerProxy<ProductsController>(browser);
 
    var mockedTextBox = MockRepository.GenerateMock<ITextField>();
 
    browser.Stub(e => e.Element("Name")).Return(mockedTextBox);
    browser.Stub(e => e.Element("Price")).Return(mockedTextBox);
 
    IForm mockedForm = MockDefaultForm();
 
 
    productsController.SubmitForm<CreateProductForm>(form =>
                                                         {
                                                             form.Name = "Product name";
                                                             form.Price = 100.0;
                                                         });
 
    mockedTextBox.AssertWasCalled(x => x.Value = "Product name");
    mockedTextBox.AssertWasCalled(x => x.Value = "100");
    mockedForm.AssertWasCalled(x => x.Submit());
}

Behind the scenes we extract data from our “form” object and use WatiN to simulate the user input to the browser. And finally we submits the form.

Conclusion

Combining the above we can write strongly typed integration test for the creation of an article in an online cash register like this:

controller.ExecuteAction(x => x.CreateArticle())
    .SubmitForm<CreateArticleCommand>(formData =>
                                          {
                                              formData.Price = 100.0;
                                              formData.Name = articleName;
                                              formData.ArticleNumber = "123123";
                                          })
    .AssertViewData<ViewArticleViewModel>(viewData => viewData.Name == articleName);

 

I hope this post will give you some ideas on how to use lambdas and the strongly typed way of programming Asp.Net MVC to help you write more robust and refactoring friendly integration test using WatiN. This technique will also work with other MVC frameworks such as Fubu Mvc etc.

Source can be found here if you are interested.

As always comments and suggestions are welcome!