For as long as I can remember, my approach to UI development has been centered around carving a UI up into it’s logical pieces (“controls” you might say) and hosting them in some kind of test harness, where I can manipulate their API’s as I continue to craft them.
I’m sure this is common practice…it’s such an obvious thing to do. I think it’s possibly not as wide spread as it might be as it’s a lot harder to do in the web (with HTML) than it is with a rich client….until now that is (read on to find out why).
For me the important design goals when building a tool to assist here are making this both ridiculously easy AND the outcome permanent.
Permanent in that the tests hang around. All too often make-shift harnesses are erected while a control is under development, then thrown away, abandoned or lost. But unit-testing taught us that having a long lived set of tests that can be run at any time (and often) provides increasing returns over the long run.
And Easy in that it’s simpler and faster to build your control this way that it is hacking anything else up. It needs to be no trouble so that you actually do it.
For me, with UI it’s turtles all the way up and all the way down. “Controls” can sit on a spectrum anywhere from super primitive (a button, a checkbox) up to the root of the application’s UI…the super aggregate of all controls, and all modules that make it up. I like to be able to bring up any piece of the UI – strip it’s behavior away, or turn it back on, hand it a mock, prod it and pock it. This in the same spirit that leads one to the set of patterns represented by the “Composite Application” approach (or see here for a great set of tool and guidance to this).
Now, you may of course be be much smarter than me (you probably are), but if I can’t do this, if I can’t factor out UI development in this clean, super decoupled fashion, I just know where I’m heading, and heading fast…..to a horrific realm of fear and loathing.
But the world need not be such an ugly place….no! Not if you have a “Development Dashboard” (this the fancy name my friend John came up with for “Test Harness”). And with the advent of Silverlight I’ve been at it again…building a test-harness to make building beautiful UI easy and fun and joyful, and I’d like to share with you some of this work-in-progress. Here’s what it looks like:
The approach of writing visual-tests against a UI control enduces many of the benefits found in classic TDD (or BDD), only with the particular slant being on UI componentry. It’s the same basic mind bend going on, namely: write the code that consumes the system first, then write the actual system. And the wonderful miracle that occurs from this seemingly counter-intuitive move is that the emergent code just tends to be cleaner, lighter, more de-coupled, and more sensible from the user’s perspective… In other words, better designed.
[Please note, by “user” here I mean the next developer working with the control…I’m not saying anything about the interaction-design or usability…that’s another story].
To get a test up in the Dashboard, all you need to do is adorn it with an attribute [ViewTestClass]
The ViewTestClass attribute signals that this is a class that contains tests to host within the Dashboard. The developer then selects the assembly that contains the test using the Dashboard’s Module Chooser:
The assembly is now loaded into the dashboard, and our new ViewTestClass name ‘MyControlTest’ is listed. Notice that we did not have to muck around with configuration files and mapping or anything like that. We just created a simple class using minimal native syntax the developer is used to, and whamo, it shows up…no fuss.
Also, now that this assembly is loaded, the Dashboard remembers it as a possible point of interest to test with, and stores a reference to it making it available each time the dashboard is loaded. It does not, however, load the assembly into memory until a test control is asked for – important for saving resources and keeping the experience light and fast. When the assembly is no longer under test it can be removed by using the “Eject” button.
Once the ViewTestClass is selected, it is added to the ‘Recent Selections’ list. As you know, the development process is a loop of micro-iterations. Write-code => test => write-code … (repeat).
The last set of test classes is positioned in a stable part of the screen, the “Recent Selections” list. Muscle memory kicks in, and no cognitive friction is incurred getting to the test control of interest, meaning the developer can remain totally in the flow with their UI problem.
Now, here’s the interesting part. To get a control into the test, we simply add a method decorated with the [ViewTest] attribute.
The type of the parameter, in this case a ‘Placeholder’ control, determines what control will be hosted within the Dashboard’s test surface. With this ViewTest in place we now have this to work with:
The ‘My Test Control’ becomes a clickable option, and the control we are testing (a ‘Placeholder’ control which is our arbitrary example) is shown on the test surface. The test method would typically manipulate the control is some interesting way, exercising the API and allowing the developer to see what’s going on, what needs fixing, and what next creative moves to make.
At this point you may also create a View-Model instance and bind the control to it if you’re using the MVVM design pattern. You can inject stub model’s into the V-M, and generally engage in the full gamut of testing-goodness.
If we need to test more than one control at a time, because we’re interested in the interaction between multiple different controls, we simply add more parameters to our test method.
…and the Dashboard lays them all out in on the test surface.