Thursday, July 10, 2008

TDD and User Interfaces Revisited

In my original post on the subject I considered the role of TDD in user interface development. There is an interesting post by Dean Wampler that also considers the subject and its interesting to get his perspective. The best thing to do when not sure about something in this business is to think for yourself - keep an open mind. Dean postulates that one should consider what to test and what NOT to test when test driving UIs. I would be interested to hear what Uncle Bob thinks about this, and whether it fits with his strong views on TDD, but I feel that much UI development should be left as nimble as possible - in fact some UI could be considered throw away - yet still built to production quality - but very easily changeable.

While business rules can and do change, the user presentation should be able to change with a quick click of the mouse in front of the customer. In fact, why not be able to have multiple flavors of UI, for different types of user/customer? I know this will be somewhat provocative, but I just don't see the practicality of test driving everything in the presentation layer. Of course, I still believe in automated acceptance testing - and this can still be test driven - just perhaps removing the emphasis on unit. What is a UI unit anyway?

I am playing devils advocate a little here, but it would be interesting to hear more opinions.

1 comment:

Paul said...

Hi Andy,

This is a tricky one. The word "Test" is heavily overloaded. Depending on your goal you need to do different types of testing. So for Human Computer Interaction (HCI)testing then a "Unit" test isn't going to help much. What you want is rapid prototyping and "hands-on" testing by your user.

For Automated "Customer Acceptance Testing", or what use to be called black box "functional testing" then you want a means of testing the functionality of the system from the outside irrespective of the look and feel. Using something like Fit/Fitness can achieve this independently of the UI. Then lastly you want to know whether your UI code works, especially if it tends to break a lot ("Test everything that can possibly break"). So for Web applications then you want a templating engine that you can use within your Unit Tests (JUnit) and a means of asserting that dynamic content has been rendered correctly. Templating engines like Velcoity and FreeMaker are good choices here (I really like FreeMaker), with XmlUnit and a bit of work you can roll your own test fixture for web pages.

Then there is desktop UI testing like Swing. I've never used one, but apparently there are one or two unit testing frameworks on the market that address this area too.

Lastly there is code-on-demand that runs in the browser. Yes Javascript. Well there are VMs that you can use to run javascript outside the browser (Rhino) and I believe there is an xUnit framework for Javascript too. Again, if you've got a lot of Javascript and you find that it breaks a lot then you need to write tests :)

Testing can be a huge investment. My basic approach is to try and get the biggest bang for my buck.

So for HCI testing, why not prototype with a flip chart and post-it notes with your customer present? I learn't this approach from a clever guy I worked with once :) You can cover a lot of ground this way at very little expense.

For black box functional testing, then forgetting the UI for a moment and focusing on the domain has its benefits. It allows you to perform test driven requirements capture which is what Fit/Fitness is all about. Personally I haven't had a good experience with Fit, but I know of others that swear by it. For me, I like the idea of killing two birds with one stone. Functional testing and UI testing in one go. This is why I go for Watir. If you write your tests well, then you can avoid them being tied to the specific look and feel ofm the UI. Also you can produce a DSL that allows you to test your domain from the UI. So if UI representations of domain concepts change, these changes should easily isolated in your tests if they adhere to the DRY principle. I like to setup the database, run a UI tests and query the UI and the db for results all within one test case. Recently I've been obliged to use Selenium, but now having used both I would go for Watir
every time.

Lastly, I would say at least a third of the effort in todays template based web applications is spent debugging the template, so I would definitely use Freemarker and XmlUnit here. I would steer well clear of JSP since its untestable, and I would do steer well clear of any web framework that didn't have a good testing story. Spring MVC and Webwork are fine.

So their you have it. Lots of options and no clear answers. I think there are a few principles though:

1. Divide and conquer. Decide what your are testing for and why. Adopt a suitable test strategy for each separate concern at the outset. Trying to retrofit tests doesn't work.
2. Test everything that can possibly break. Know (discover) where you are likely to introduce bugs, and have tests ready to detect them early. If you've got lots of different technologies that can break then testing will be costly.
3. Spend as little on testing as you possible can. Test are not production code. A solution that is expensive to test is a design smell. Keep it simple. If you've got a lot of javascript and AJAX that is expensive to test, then don't use Javascript and AJAX. If you can cover both UI testing and functional testing using a single Watir test then do so and only use Fit where the functionality is not testable from the UI. Don't confuse "cost effective testing" with "no testing". Without tests you do not know whether our code is production quality and whether you can ship it. So no tests = no iterative development. On the other hand we don't get paid for shipping tests, we get paid for shipping production quality code. So be sensitive to how much time you are spending writing and maintaining tests. be willing to ask the question is it worth it? and could I get a bigger bang for my buck another way?

My 2 cents.

Paul.