Freigeben über


Practical unit testing of graphical user interfaces

When I last wrote about TDD and GUI I was talking quite vaguely about how TDD of GUIs can work. Since then I've thought more on the practical details and by chance the book I was reading at the time (Testdriven by Lasse Koskela) covered this topic. Inspired by that book I've done a few experiments.

First of all we must decide on what we want to test in the GUI. Usability can never be tested only by applying TDD/BDD. So we don't want to test the looks of the GUI. We should test the functionality of the GUI. There is also two types of graphical components we will be testing. There are user controls where we want to test that certain pixels turn out the way we want and there are other types of graphical components.

Let's first look at graphical components where we actually want to check pixel colors. What could this be? Well, it could be a custom graph component or a progress bar. Something where we easily can know from how we setup the test that certain pixels will be of a certain color. To test this we should render the user control to an in-memory bitmap and check the color. Using WPF this is pretty easy using the Render method. Using WinForms we have to use different methods depending on if it is a form or control we're testing. The reason forms are different is that depending on the layout it can be impossible to know the exact offset that should be used so the same method as used for controls cannot be used. On the other hand you should probably not test complete forms by drawing them to a bitmap anyway...

    1:          public void PaintControl(Control control, Bitmap b)
   2:          {
   3:              Point offset = GetClientOffset(control);
   4:              foreach (Control c in control.Controls)
   5:              {
   6:                  c.DrawToBitmap(b, new Rectangle(new Point(offset.X + c.Location.X, offset.Y + c.Location.Y), c.Size));
   7:                  PaintControl(c, b);
   8:              }
   9:          }

The offset code would not be needed if we can use the protected InvokePaint method.

So what about all controls where exact pixel match is not needed, what should we test? Well we can always test relative positioning if it makes sense. Things like "the OK button is always above all other buttons" could be tested if we find that useful. Other than that we should focus on testing that the functionality of the user interface is correct. In order to do this and to make our GUI easily tested I think the MVC (Model-View-Controller) pattern is a great candidate. And probably the best is the variant called MVP (Model-View-Presenter) with a passive view. The model is a no-brainer from a TDD perspective. The presenter should be as easy to test. And with a passive view that one should be pretty easy to test too.