Automated testing, especially at an acceptance level, can be a bear with WinForms applications. On my last project we effectively abandoned integrated FIT testing against the UI. We tried to go down a path of writing Window Driver's against each big control, and driving that through FIT & NUnit tests with NUnitForms. It just turned out to be too much work, too much duplication of information, and just plain laborious.
There are definitely some things you can do to enhance testability. The various flavors of Model View Presenter come to mind immediately, but that can easily bring on some repetitive coding around filling dropdown lists, validation, and coordination with views.
I'm going off the beaten path in my new WinForms project. I'm ditching WinForms data binding altogether because it plays utter havoc with automated testing. Instead, I'm building my own programmatic data binding that effectively embeds a Window Driver class directly into each User Control. The "ScreenBinder" class is configured programmatically with a fluent interface (Martin says the best way to understand the limits of a technique is to overuse it;).
I'm trying to kill 4-5 birds with one stone:
- That fluent interface contains grammars to declaratively attach properties of the model class to elements on the page. In effect, I'm recreating data binding, but in this approach I'm going to have explicit control over when things happen. No more goofy hocus pocus focus stuff to get data binding to fire inside tests.
- The ScreenBinder will also double as a Window Driver that I can use inside automated tests to find screen elements by either the bound field name or the attached label name. I'm already building a reusable DoFixture right off the bat that can act as the foundation for a testing DSL to express screen requirements in automated tests. On my previous project we were routinely killed by "oh yeah, this element should default to XYZ when ABC happens like the legacy system" type of requirements. This time around I want to be able to capture that stuff upfront in human readable FIT tests that can be run in the automated builds.
- Declaratively add minor screen behaviors like filling a combo box with data from the service layer with a grammar like "Bind(SomeConstantClass.Category).To(categoryComboBox).WithLabel(categoryLabel).FillWithList(ListType.Category)" Basically, I'm hoping to have a single abstract method that's overriden in each UserControl that declaratively defines much of the behavior of the screen. One of my design goals is to be able to quickly understand the behavior of a screen largely by reading through this method.
- Wait, you're putting more behavior in the screen! Isn't that wrong? Well generally I'd say yes, but the underlying goal is to make code easy to write, change, read, and test. I'm using what I call the "Micro-Controller" pattern to define little reusable controller classes to drive one element at a time. Those little micro-controller classes can be TDD'ed to my heart's content.
- I'm building a quick test harness with NUnitForms to make declarative assertions against one of my BoundControl's for common functionality. I'm figuring that by making the screens easier to test inside of NUnit that I can get away with thinner Presenter's. I'll probably go with a Supervising Controller for more complex logic and dealing with the service layer.
- The ScreenBinder ends up being a great way to bind declarative validation rules to the proper screen elements. I simply capture a Notification object that relates validation messages to the property of the Model class. The ScreenBinder knows the correct element for a given Model property, so it's not that hard to go the extra step to attach validation messages to the proper screen elements. On my last project one of my client developers built something similar to the new Validation Block that used attributes on our domain model classes to enforce validation logic. We did use the BindingSource on the screen to attach validation messages to the proper screen element. I'm going to do the same thing here, but use a Notification object in between to make validation logic easier to test.
So, the question I ask you, gentle reader, is how crackpot is this idea? If it works I'm thinking about harvesting a new OSS project out of this work (after DevTeach and a solid StoryTeller beta and assuming that the client is okay with it). Otherwise, if I never post about it again it's a good bet that it failed.
There's no possible way that it would fly with my client, but I wonder how much better this approach would be if I could drive the UI with the RubyCLR bridge so I could use the declarative ActiveRecord validation scheme and symbol. I think the Fluent Interface approach would be more effective in C# 3 with the ability to use extension methods to add application specific additions to the fluent interface.
I'm working solo at the minute, but I'm going to be joined by another Finetix developer soon. One of the first things I'm going to do when he onboards is get his opinion on the "novelty" portions of my envisioned approach. If that approach doesn't fly with him, it probably gets scrapped. And if he's enthusiastic, then we still need to be careful because then we're just guilty of group think.
Posted
Mon, Apr 16 2007 9:21 PM
by
Jeremy D. Miller