Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

The usefulness of interaction tests, or “How to question the method”

More and more often these days, I’ve been having a crisis of faith conscience when it comes to TDD, or rather, interaction testing in general. It happens more so on projects where I’m the only developer or there is a very small team of strong developers. And with the latest project being in GWT, where you are practically beaten down by all the guidance espousing separation of concerns, it’s even more pronounced.

Consider a screen that display a list of pet names for your truck. When you click the “add” button, it displays a dialogue box with a blank form. If you’re using an MVP pattern, this is likely what would happen when the page is loaded, from a testing point of view:

  • Load a list of data from the datastore
  • When it’s retrieved, send the list to the view
  • Add a click handler to the “add” button that does something

In this case, I’m using gwt-dispatch, which is a command pattern implementation for GWT. So the first point is executed by creating a command and dispatching it like so:

dispatcher.execute( new GetPetNamesCommand(), new GetPetNamesCallback( ));

The GetPetNamesCallback class looks like so:

public class GetPetNamesCallback extends AsyncCallback<GetPetNamesResult>
{
   @Override
   public void callback( GetPetNamesResult result ) {
      display.showList( result.getPetNames( ) );
   }
} 

And the code to add the click handler when the page is loaded:

display.getAddNewLink().addClickHandler(new AddNewClickHandler( ));

(This is a passive view, I think, so the presenter handles all the event wire-up.)

Going to the testing side of things, I have a test context class called When_initializing_pet_names_list_presenter and within it, I have these tests:

  • should_request_data_from_data_store
  • should_wire_up_add_new_button

In each of these tests, I dutifully go about my business mocking out a view and a dispatcher and creating a presenter. I set up expectations and the real meat of the test is in this line (using Mockito syntax):

verify( dispatcher ).execute( isA( GetPetNamesCommand.class ), isA( GetPetNamesCallback.class ) );

That is, we verify that we executed the dispatcher with appropriate arguments. There’s a similar test for the second method and in another class, When_pet_names_have_been_retrieved, there’s a method that tests the GetPetNamesCallback class to ensure, also dutifully, that display.showList was called.

When creating this code, I did it the other way around, writing the tests first and fleshing out the details. That makes no never mind here. The end result is the same no matter which way you go about it.

Where my crisis of conscience comes in is when I review the code I’ve written based on these tests. This screen isn’t very complicated. If I were to bang out the code for it without tests, I can’t imagine doing it any other way.

But someone else could, of course. And maybe they aren’t as disciplined as I am about keeping concerns separated. Fair enough. At the moment, they aren’t working on my project though. It’s just me and it will be me for the foreseeable future. Besides which, I’ve done enough thinking on how smart or dumb the next guy is going to be.

Furthermore, these tests feel too granular. Like they’re too focused on implementation and that they’d break at the slightest refactoring. Like say, if we switched to an architecture where the view wires up its click handler and raises events. Now I have to go and fix all these tests.

Maybe that’s short-term thinking and it will lead to a lapse in judgment one day where I write untestable code to get something out the door.

Maybe that’s true but maybe it’s not. I don’t think I’m advocating against interaction testing in any case. Just wonder what value it holds once you’ve been doing it long enough that you can’t write untestable code if you tried, even if there aren’t any actual tests.

In any case, this is just interaction testing. There are plenty of cases where unit testing can be useful in verifying complex business rules (like whether it’s okay to have duplicate pet names for both your truck and your sister-wife).

What I see more value in is a higher level integration tests. Having recently taking Selenium for a spell, it seems more useful. What happens when the user lands on the “Pet Names List” page? Why, it should have this text showing in its HTML somewhere. What about when you click the “add” button? Well, then the page should include a form in it.

In this case, if anything changes in the bowels of the application, these tests remain untouched and, more importantly, still very relevant. If you have to change these tests, it’s as the result of a user interaction change and I think that warrants a change in your tests more than which flavour of MVP you decide to use.

This isn’t really a position I’ll defend to the death, or even to the slightly injured. As it is, I’m not altogether happy about how I’ve explained my position. Should probably let it sit for a bit but I don’t have that kind of patience. In any case, more and more when I run my test suite and see everything running green, I feel a little bit Pavlovian.

Kyle the Trained

This entry was posted in Mocking, TDD. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://geekswithblogs.net/chrisfalter Chris Falter

    Of course, like any approach, automated acceptance tests/Behavior-Driven Development has costs and difficulties. It just seemed to be a better fit for the situation you are describing, Kyle.

  • http://geekswithblogs.net/chrisfalter Chris Falter

    I would agree with Steve Py that unit tests should not cover views, but domain logic. For views, I think you want to use the BDD (Behavior-Driven Development) approach with a tool like Fitnesse. Here the terms are expressed in terms such as:

    * when I click the x button, I expect the page to include a certain form.

    * when I land on a certain page, I expect the title to be “y.”

    In theory the presenter would expose an interface to the view, and the developer could write fixtures that link the test to the presenter’s interface.

    This approach would have many advantages over your current approach, Kyle:

    * Designers and customers can be involved in formulating the specification/tests

    * As long as the presenter’s interface doesn’t change, the details of its implementation can be refactored as necessary.

    * You have automated tests for the behavior of the system.

    * Those automated tests are not expressed in terms of UI primitives (e.g., control names), so you can even change the UI implementation without breaking the behavioral tests.

  • fschwiet

    added to http://codebetter.com/blogs/kyle.baley/archive/2010/02/26/the-usefulness-of-interaction-tests-or-how-to-question-the-method.aspx?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+CodeBetter+%28CodeBetter.Com%29&utm_content=Google+Reader&fds:

    You’ve left out a lot of code so I can’t give feedback on all of it. But I think you could improve this by using state verification rather than behavior verification (mocks). In your blog you says you dutifully mock out the dependencies, it might be worth spending time trying to avoid mocks and instead use the original objects.

    For instance, I think you’d be better served not mocking GetPetNamesResult when testing GetPetNamesCallback.callback(). Instead instantiate an instance in the test, and use a public method to set the pet names. (probably such a setter function or constructor already exists).

    Second, this code should be generating a model before interacting with the view. Your test for GetPetNamesCallback.callback() should not mock member GetPetNamesCallback.display but rather make assertions against the data it has accumulated in the model. Maybe member GetPetNamesCallback.display contains that model, in that case rather than mock the display object you can make verification checks against the model representation.

    I think this improves the tests because your test is written in terms of what these pieces are (only if the actually collection or model types change will an update be needed) rather than specific accessor methods (which will necessarily change if the collection or model type changes, but may change for other reasons).

  • fschwiet

    You’ve left out a lot of code so I can’t give feedback on all of it. But I think you could improve this by using state verification rather than behavior verification (mocks). In your blog you says you dutifully mock out the dependencies, it might be worth spending time trying to avoid mocks and instead use the original objects.

    For instance, I think you’d be better served not mocking GetPetNamesResult when testing GetPetNamesCallback.callback(). Instead instantiate an instance in the test, and use a public method to set the pet names. (probably such a setter function or constructor already exists).

    Second, this code should be generating a model before interacting with the view. Your test for GetPetNamesCallback.callback() should not mock member GetPetNamesCallback.display but rather make assertions against the data it has accumulated in the model. Maybe member GetPetNamesCallback.display contains that model, in that case rather than mock the display object you can make verification checks against the model representation.

    I think this improves the tests because your test is written in terms of what these pieces are (only if the actually collection or model types change will an update be needed) rather than specific accessor methods (which will necessarily change if the collection or model type changes, but may change for other reasons).

  • http://bjarte.com BjartN

    ..in any case I think it is valuable for a developer to come to this conclusion the way you have, by (possibly) testing too much, and then relaxing a little bit. This way you have more information when deciding what not to test. :)

  • Steve Py

    A general rule I follow: “Unit Tests do not test views.”

    Unit tests test that when my domain is told to create an object, it creates a valid one. That when it’s told to validate an object, it does it’s job properly. They test that when my converters are called to format objects, they format the data properly.

    They do not test that presenter A created Button X on View Y. Again for the previously stated reasons is that it causes way too much friction for re-factoring.

    Just as my unit tests are designed to only test public functionality (not private implementation details) they test business logic and application behaviour without constraining to a particular view representation.

    The goal of course is to automate as much as possible, but some things you cannot easily replicate the “feeling” when running through an application. Making sure an application behaves for a user is something I try to do myself, mimicking a user. I think humans should always test human usable interfaces.

  • JohnV

    This question reminds me of two posts I ran across recently.

    Two kinds of views
    http://blog.thecodewhisperer.com/post/207813060/two-kinds-of-views

    and

    An argument for moving ASP.NET MVC controllers to a separate assembly
    http://devlicio.us/blogs/billy_mccafferty/archive/2009/01/09/an-argument-for-moving-asp-net-mvc-controllers-to-a-separate-assembly.aspx

    Both posts are talking about logical seperations.

  • http://blog.functionalfun.net Samuel Jack

    Kyle, Having religiously written Unit Tests for the ViewModels in my Model-View-ViewModel architecture for the last 18 months, I think I’m beginning to think along the same lines as you: Unit tests at this level seem to add friction to the development process without a significant amount of benefit.

    I’ve just started reading an excellent book on TDD: Growing Object Orientated Software Guided by Tests (http://www.growing-object-oriented-software.com/). One of the first things that struck me about the authors’ approach is that it is possible to drive your design by writing an integration test first (automating the UI with Selenium, or UIAutomation or WindowLicker or whatever). TDD isn’t just about Unit testing.

  • http://MurrayOn.NET/ Mike Murray

    Your thoughts seems similar to a discussion Dave Tchepak and I have been having via blog posts and comments. Here are his two most recent posts on the subject (with plenty of comments from me):

    http://www.davesquared.net/2010/02/what-exactly-is-tdd-driving.html

    and

    http://www.davesquared.net/2010/02/improving-tdd-skills-via-treatment-of.html

  • Michael

    We switched from unit tests to higher-level integration tests a year ago and have been so much happier. Exactly like you mentioned, the unit tests were becoming too granular and any change to the way the backend worked caused massive test failures.

    Now our test suite concentrates on frontend-level concepts (split between UI tests and ‘messaging’ tests). Data in == data out. So massive refactoring can be accomplished without affecting a single test. The place where it has really helped is in customer bug reports. We can write a new ‘customer-facing’ test that verifies the error, and fix it. The only drawback we have run into is since we are testing the entire stack including a live DB, the tests (of course) run slower, but from our perspective the benefits far outweigh this.