Random header image... Refresh for more!

Category — Software Testing

Test-Driven Disaster

When first considering using Test-Driven Development, many people will consult their local tester.  This is, of course, the wrong thing to do, because their local tester doesn’t actually care about Test-Driven Development.  It’s not a testing methodology, it’s a development methodology.  Asking a tester about it because it has the word “Test” in it is just as wrong as asking a bus driver about it because it has the word “Drive” in it.  And if you’re assigning your tester the task of “Test-Driven Development”, you need to stop before you damage something, because you’re doing it wrong.

For those who don’t know, “Test-Driven Development” is an Agile methodology for designing classes and developing code, where you begin with writing an automated test, stubbing out a method as you go, then after you have the test, you implement the method until the test case passes.  What many people seem to miss is that testing is merely a side-effect, it’s not the central goal.  Instead, the goal is to develop code from the top down by looking at how you’re going to use it.  There’s virtually no difference between TDD and stubbing out a low level module while implementing a higher level module.  The approach is the same:  You focus on how you’re going to use the functions you’re writing by actually using them, rather than trying to list all the operations you might need without the context on how they’ll be called.  If you don’t understand that’s what you’re doing, you’re bound to foul it up and hurt something.  You can’t do TDD after you’ve already specced out the method signatures, so don’t even try.

Beyond the rampant misunderstanding regarding what TDD is, my biggest reservation about it is the fact that most people who want to try it don’t know how to write tests.  Bad tests are far worse than no tests.  By strictly adhering to the principles behind TDD, you’ll write five tests and think you have everything covered because you red-green-refactored your way to perfection.   In fact, since TDD and the Wide World of Agile will encourage you to use code coverage you’re guaranteed to have tested everything.  Trouble is, you’ve only covered five specific cases, not the five hundred cases that would be apparent if you actually looked at the problem and nowhere near the five thousand cases your users will throw your way.  Code coverage will only tell you that you’ve executed lines of code, not that they’re correctly executing according to your plan.  By exactly following the process of Test-Driven Development, you’re pretty much assured to write nice shiny code that doesn’t actually work, even though it passes all of your tests.  To successfully write effective unit tests, you have to go beyond the initial requirements (Which are incomplete) and the initial design (Which has changed) and look at the solution you actually implemented to see where the problems are.  In other words, your job isn’t over after “Refactor”, you still need to go back and enhance your initial suite of tests before you’re done.

Take, for example, the requirement that you write a function that takes any two numbers and returns their sums.  In the world of TDD, you start with a test:

[Test]
public void TestSumMethod()
{
    Assert.AreEqual(5, SumMethod(2, 3), "2 + 3 = 5 Check");
}

Okay, so, we have the test.  Now we need the function stub.

public int SumMethod(int a, int b)
{
    return 0;
}

Compile, run tests, and blammo!  Your test failed, as it should, because you haven’t implemented anything yet.  This was the “Red” step.  Now it’s time to go Green.  For that, you implement the function you stubbed out.

public int SumMethod(int a, int b)
{
    return a + b;
}

Run the test again and it’ll pass.  Run code coverage and -look at that- 100%.  Now you refactor, rerun the tests and code coverage, and everything’s green, so you’re done!

Except…  You’re not done.  The requirements said “Any two numbers”.  So, you need more tests.  Does it work with negatives?  Big numbers?  Small numbers?  Any tester worth paying will immediately break your function by trying to pass in 2147483647, 2147483647.  And that’s just the beginning.  What about real numbers?  Can it do π + e?  And let’s not even get into complex numbers.  During your refactoring, did you change the inputs to a type that includes NaNs and Infinities?

The point is that following Test-Driven Development left you thinking that you had written a function that was adequately tested, when in reality, it was woefully under-tested.  Obviously, this was a simplified example.  The consequences of doing this with some real, worthwhile code could be disastrous.

Now, I like developers writing unit tests (Well, I like it when they write good unit tests, as I’ve already written…).  I don’t care if they come first, last, or during.  Just don’t fool yourself into thinking that practicing TDD will mean that you’ll have all of your unit tests written as part of your red-green-refactor cycle.

All in all, I like what TDD tries to do.  Thinking about code before you write it and writing unit tests are generally good things to do.  What I don’t like is the shiny glowing path to failure that TDD sets out for those who are unprepared.  I have no doubt that certain Agile practitioners can do TDD and do TDD well.  Unfortunately, I don’t think those people live in The Real World.  If you run your own software consulting firm, then great, go off and do things how you want.  But for everyone else, you’re going to get fired if you try to do that sort of thing, because you’re going to do it wrong and waste a lot of time in the process.

November 4, 2009   No Comments

SWA in WPF and Silverlight

A couple of weeks ago, I wrote about using the System.Windows.Automation libraries in the .Net Framework to write UI Automation, primarily for use in writing automated test cases.  Well, that’s only half the story.  In order to truly unlock the power of SWA and the UIAutomation library, you’ll want to add SWA instrumentation to your application.  It’s not required for using SWA, but believe me, it will make your testers happier.

Microsoft has provided a way to add enhanced support for UIA into Win32 and Windows Forms apps, but WPF and Silverlight have been built from the ground up to have full and simple support for the UI Automation properies.  In this post, I’ll be exploring how to instrument a Silverlight application, largely because I can give a link to my finished sample app for you to explore.  Working with WPF should be mostly the same. 1

Let’s start by looking at what you get from Silverlight, straight out of the box.  We’ll begin with a simple text box and button, the sort of thing you’ll find in pretty much any UI program ever written.

<StackPanel x:Name="LayoutRoot" Orientation="Horizontal" Width="250" Height="32">
    <TextBox x:Name="TextEntryBox" Width="150"/>
    <Button x:Name="SubmitButton" Content="Submit"/>
</StackPanel>

That produces a super exciting display that looks like this:

SWASilverlightApp1

I has mad UX skillz.  Fear them.

If you open good old trusty UISpy and examine the control tree of your XAML masterpiece, you’ll find something that looks a bit like this:

SWASilverlightApp-UISpy1

An “edit” control and a “button” control in a Silverlight Control?  That looks an awful lot like what we just wrote up in XAML, doesn’t it?  That’s because it is.  Yep.  You write it and presto, it’s seen by SWA.  And if you look at the automation properties panel in UISpy, you’ll see that the edit control has an AutomationId with the value of “TextEntryBox” and that the button control has an AutomationId of “”SubmitButton”.  Those are also what we put in the x:Name attributes back in the XAML.  In other words, for Silverlight and WPF apps, most of your controls will automatically be exposed to UIAutomation using the programmatic names you’ve already given them in your code.  You get that for free.

But wait!  There’s more!

Do you see all those other properties in UISpy?  Well, if you don’t, here’s a screenshot for your enlightenment:

SWASilverlightApp-UISpy2

Well, you have direct access to set some of them straight from XAML.  Okay, so it’s not completely free, but it’s still really really easy.  They’re exposed through the AutomationProperties attribute.  As an example, let’s set the “Name” property of the text box in the sample.  If you look at the UISpy control tree, you’ll see that it’s called “”, instead of anything useful.  The automatic population of the “Name” property usually uses the content of a control.  In the case of the button, it has a text label of “Submit”, which is where it gets the name from.  But the text box doesn’t have any content, so it gets left out.  Let’s give it a name before it gets depressed.

<TextBox x:Name="TextEntryBox" Width="150" AutomationProperties.Name="Search Query"/>

All it needs is that “AutomationProperties.Name” attribute set and there you have it.  our edit box is now showing up with a name in UISpy and everyone’s happy.

SWASilverlightApp-UISpy3

Most of the time, I’ll stick to “Name” and “AutomationId” for helping me find elements for my automated tests.

For a good number of decently simple applications, that’s all you’ll need to know.  For that reason, most tutorials I’ve seen on the subject stop here and pretend that this much information is actually adequate for using SWA in the real world.  I, however, have actually done some work in the real world and know that this alone can be painful to work with.  So, I’m going to keep rambling for a bit.

Consider, for a moment, that you’ve assembled some combination of controls that is useful to you, and that you’d like to reuse that combination of controls and not have to copy/paste/rename.  Now, I understand that many of you will never attempt something as wild and complex as this, and that the thought of it alone might drive you to madness, but please bear with me, as it’s required for my next demonstration.  For the sake of conversation, let’s say that this useful combination of controls is a text box and a button to go with it.

So, you have a user control with a box and a button.  I’ve given it the bleedingly obvious name of “BoxAndButton”.  This code should look familar to you:

<UserControl x:Class="SWASilverlightApp.BoxAndButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="250" Height="32">
    <StackPanel x:Name="LayoutRoot" Orientation="Horizontal">
        <TextBox x:Name="TextEntryBox" Width="150" AutomationProperties.Name="Search Query"/>
        <Button x:Name="SubmitButton" Content="Submit"/>
    </StackPanel>
</UserControl>

Okay, now that we have the control, let’s use it somewhere.  In fact, it’s a reusable control, so let’s use it twice, just because we can!

<StackPanel x:Name="LayoutRoot">
    <MathPirate:BoxAndButton x:Name="SearchDatabase"/>
    <MathPirate:BoxAndButton x:Name="SearchWeb"/>
</StackPanel>

(Please note the “MathPirate” namespace that you’ll have to add to your XAML file, if you’re playing the home game.)

Now we run our app and see our two glorious text boxes and buttons.

SWASilverlightApp2

Now let’s go into UISpy and see what Silverlight or WPF has given us for free.

SWASilverlightApp-UISpy4

Now, this is the point in the application development cycle where you’ve made your UI tester cry.  (Or, if they’re anything like me, you’ve made them cry and shoot you with a fully automatic Nerf gun.) While all of the elements are there, everything is jumbled together.  Despite naming one of the controls as “SearchDatabase” and the other as “SearchWeb” and using a hierarchy of controls, SWA has decided that you really want a flat list of useless.  Anyone trying to automate against this isn’t going to know which button is which, and will therefore have to rely on ordinal positions of controls and a whole lot of luck.  If, for some reason, you decide that Web should come first and you go and swap the boxes, you’ll decimate every single test written against this app.  Database queries will go to the Web and Web queries will end up at the Database, and a quantum singularity will form that will destroy the entire universe.  So, let’s fix things, shall we?

I know!  I know!  AutomationProperties!

<StackPanel x:Name="LayoutRoot">
    <MathPirate:BoxAndButton x:Name="SearchDatabase" AutomationProperties.Name="Database"/>
    <MathPirate:BoxAndButton x:Name="SearchWeb" AutomationProperties.Name="Web"/>
</StackPanel>

Give it a run, and…

SWASilverlightApp-UISpy4

FAIL.

So, what gives?  You’re using a nicely hierarchical control tree, you’re giving things nice names and identifiers, why aren’t you getting anything useful from SWA?  The reason:  UIAutomation is primarily designed to expose interactive controls and elements, and your user control isn’t an interactive element in itself, only the text box and button inside it are.  It’s wrapped in a StackPanel, but things like StackPanels, Grids and Canvases in Silverlight and WPF are considered to be strictly for graphical layout and say nothing about the interaction with the system.  As a result, they don’t show up in the control tree. 2  And the BoxAndButton control itself doesn’t show up because you haven’t told SWA that it’s interesting yet.  Let’s do something about that.

The method that WPF and Silverlight use to expose these AutomationProperties is called an “AutomationPeer”.  AutomationPeer is an abstract class that has a bunch of methods for you to implement, like GetNameCore() and GetAutomationIdCore().  The idea is that these Get*Core are supposed to return the value of one of the Automation Properties for UIA to expose.  This gives you a lot of control over what gets seen by UI Automation clients or tools like UISpy.  If you like that level of control, then feel free to inherit directly from AutomationPeer and implement all thirty or so Automation Properties.  The rest of us, who don’t like tedium, will simply use the implementation provided by FrameworkElementAutomationPeer.

Of course, having an AutomationPeer isn’t going to help you much, unless you tell SWA to use it.  You do that by overriding the method OnCreateAutomationPeer() on your control class.  The method is defined on the UIElement base class, so every element in Silverlight already has it, you’ve just probably never noticed it before.  OnCreateAutomationPeer() just needs to return the appropriate AutomationPeer object that exposes your object and the Automation Properties and Control Patterns you want the world to know about.

Okay, that sounded scary and confusing, but it’s not really that bad when you see it in action.  So, let’s get to the code!  Open up the .cs file for your control.

First, add a using:

using System.Windows.Automation.Peers;

Then, override OnCreateAutomationPeer:

protected override AutomationPeer OnCreateAutomationPeer()
{
    return new FrameworkElementAutomationPeer(this);
}

And now let’s run our pretty application once more and see what UISpy has for us this time.

SWASilverlightApp-UISpy5

Boom-diggity, that’s what I’m talking about.

Now, you have a nice control tree, with your web and database search controls uniquely identified.  While your efforts may not have made the testers completely happy, at the very least, they should have stopped shooting you with the Nerf guns for a while.

But wait!  There’s more!

If you were paying attention, you may have noticed the mention of Control Patterns that I ever-so-slyly snuck in a few paragraphs back.  Control Patterns were the prime focus of the earlier article on using SWA and UIAutomation, but I haven’t really said anything about them here at all.  Why is that?  Because on the implementation side, you typically don’t have to care about them.  You’re using a text box or a button or a scroll bar or whatever that already implements all of the control patterns that it needs, for the most part.  That means you typically don’t have to do a damned thing to get the ValuePattern or InvokePattern to work, while the testers all have to suffer with that idiotic rehash of QueryInterface and COM.

Before you start gloating about that, you need to realize that I’m a tester and now I’m going to make you suffer, too.

All along, we’ve been using a Box and Button as two separate elements.  We’ve put them in a single control, though.  Why not make that single control appear as a single control to UIAutomation?  In other words, why don’t we make our custom control look like it is a text box and a button, rather than something that just contains a text box and button?3

So, somehow, we now have to implement the ValuePattern and the InvokePattern on our BoxAndButton control.  The way to do that, as mentioned before, is through the AutomationPeer.  First, we’ll have to create our own subclass of AutomationPeer.  Again, since I’m not crazy, I’m going to use FrameworkElementAutomationPeer as a base, but you don’t have to.

public class BoxAndButtonAutomationPeer : FrameworkElementAutomationPeer
{
    public BoxAndButtonAutomationPeer(BoxAndButton owner) : base(owner) { }
}

Be sure to change your OnCreateAutomationPeer in BoxAndButton to return one of these instead.  If you run this now, you should see everything act exactly the same as it was before you added this new class.

If you go inside the your new AutomationPeer class and type “public override “, you’ll conjure the magic Intellisense window that’ll show you the list of all those Get*Core() and other Automation Property methods I talked about earlier.

AutomationPeerMethods

You can implement any of them if you want to, but the implementation in FrameworkElementAutomationPeer is usually good enough.  Except for the method GetPattern…

GetPattern is the method that SWA calls on your AutomationPeer when someone using SWA calls “GetCurrentPattern” on an automation element representing your control.  You’ll need to implement it and return an object that implements whatever ControlPatterns you wish to support.  In our case, we want to implement ValueProvider for the Text Box and InvokeProvider for the Button.  The only parameter to GetPattern is a PatternInterface enum containing a value for each of the existing ControlPatterns, so a simple implementation is to switch on patternInterface and return the appropriate object.

public override object GetPattern(PatternInterface patternInterface)
{
    switch(patternInterface)
    {
        case PatternInterface.Value:
        return Owner;

        case PatternInterface.Invoke:
        return Owner;
    }

    return base.GetPattern(patternInterface);
}

If you run it right now and look at it with UISpy, you’ll see that your BoxAndButton control now is apparently supporting the Invoke Pattern and the Value Pattern, just like we want it to do.  Trouble is…  It doesn’t actually support anything.  You may have noticed that I was simply returning “Owner” from GetPattern and found that odd.  Owner is the UIElement we passed into the constructor for FrameworkElementAutomationPeer.  In other words, Owner is the BoxAndButton control itself.  But, we haven’t actually done anything to BoxAndButton to let it know that it’s supposed to support InvokePattern.  Let’s take care of that now.

Each of the control patterns is exposed through an interface. 4  For the Invoke Pattern, it’s IInvokeProvider.  For the Value Pattern, you’ll want to use IValueProvider.  These and other Provider interfaces for for the other patterns live in the System.Windows.Automation.Providers namespace, so be sure to add that.

IInvokePattern is an interface with a single method, called Invoke.  It takes no parameters and returns nothing.  The function is supposed to cause the control to perform some action.  In our case, we’re going to want it to act like the button was clicked.  Like so:

#region IInvokeProvider Members

void IInvokeProvider.Invoke()
{
    AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(SubmitButton);
    IInvokeProvider invokeProvider = (IInvokeProvider)peer.GetPattern(PatternInterface.Invoke);
    invokeProvider.Invoke();
}

#endregion

Okay, that was freaking ugly.  You see, Silverlight and WPF don’t really have a way to easily simulate a button click on the Button class.  So, this method is diving into the world of UIAutomation itself and using the InvokePattern that’s already available on the Button.  Basically, it’s a passthrough.  Making this function suck less is left as an exercise to the reader.

IValueProvider is somewhat more straightforward and sane.  It has three members.  IsReadOnly, a boolean property that returns true/false based on whether or not the control is read only.  Value, a string getter property that returns the value of the control.  And SetValue, a function that takes a string and sets the value of the control with it, because apparently the designers hadn’t heard that properties like “Value” can have both getters AND setters.

#region IValueProvider Members

bool IValueProvider.IsReadOnly
{
    get { return TextEntryBox.IsReadOnly; }
}

void IValueProvider.SetValue(string value)
{
    TextEntryBox.Text = value;
}

string IValueProvider.Value
{
    get { return TextEntryBox.Text; }
}

#endregion

 

 Conveniently, all three of those members on IValueProvider map directly to things we can easily do with a text box already.

Now, if you go into UISpy and play around, you’ll see that not only does the BoxAndButton control say that it supports the Value and Invoke patterns, but that you can now actually use them.  You can easily see the Value Pattern in action.  Simply put some text in the box, then look in UISpy, and you’ll see that the custom BoxAndButton now can see the text value.  To see that the InvokePattern is wired up properly, you’ll have to hook up an event handler on the Click event of the button.

Of course, you don’t have to do exactly what I did in my example.  You can use whatever you need and whatever you have available in the I*Provider implementations.  Do it the way that makes sense for you.   You don’t even have to implement these interfaces on the control class directly.  I did it because it was convenient in this case, but it might not make sense for you.  You can use any class you want, it’s entirely up to your design.  Just change what you return from GetPattern.  However, if you put the interfaces on the control class, like I’m doing, I would recommend explicitly implementing these interfaces, since it’s unlikely you’ll want to expose these methods to anyone using the class normally.

There’s just one little annoying thing left about the control.  If you look in UISpy, you’ll see that our BoxAndButton control now supports the Invoke and Value patterns, but we still have the Text Box and Button as children, and those also have the Value and Invoke patterns on them.  They’re not needed anymore.  Let’s axe them, shall we?

If you’ll look at the list of functions available to override on our AutomationPeer, you’ll see one called GetChildrenCore.  You can override that function to tell UIA what your children should be.

protected override List GetChildrenCore()
{
    return new List<AutomationPeer>();
}

Obviously, it’s more useful to tell the system that you have children that it wouldn’t ordinarily know about (Like in the case of an owner drawn control), than it is to disown the children that you do have.  So, when using this function in a normal situation, you’re probably going to add something to the list that’s being returned.

Now, if you run the app and look at it in UISpy, you’ll see that the children of the BoxAndButton control have been written out of the will.  You’ll also likely see that, in fact, UISpy is having trouble finding our control.

Element : Element details not available.
Name : TreeValidationException
Message : UI Automation tree navigation is broken. The parent of one of the descendants exist but the descendant is not the child of the parent
Stack Trace : at UISpy.Base.Tree.BuildTree(AutomationElement element)
    at UISpy.Base.Tree.BuildTreeAndRelatedTrees(AutomationElement element)

Well now…  That sucks.  See, what’s happening is that UISpy is finding the real Text Box and real Button controls that still exist and trying to select them, but it’s unable to find their parents in the tree.  In other words, we’ve tampered with the natural order of things and are making UISpy go all loopy.  Good luck with that, I’m gonna end on this high note.

You can find my sample application here (A version that’s not hOrking UISpy):  http://mathpirate.net/log/UploadedFiles/SWASilverlightApp1/SWASilverlightAppTestPage.html

You can get the source code out of SVN here:  http://www.mathpirate.net/svn/Projects/SWAExample/SWASilverlightApp/

  1. I haven’t done anything with SWA and Windows Forms or Win32 yet, so I don’t know what that entails, but from a quick glance at the docs, it’s a bit more complicated. []
  2. And honestly, you don’t want them to.  It would be insane to try to navigate through all the different panels that appear everywhere.  One of the Silverlight 2 Betas had all of the panels and things exposed and it was frightening. []
  3. Why don’t we?  Because it’s actually useless to do so.  A text box and a button are perfetly good controls to contain.  However, treat this as an exercise in exposing a completely custom owner drawn control to System.Windows.Automation. []
  4. If you’ll remember from the previous article, a large serving of wrath was thrown toward the designers of SWA for not using interfaces so finding out that they do, in fact, use interfaces on objects internally was a bit of a relief, but also upsetting.  I still can’t understand why the person responsible for the design on this side didn’t smack the designers of the consuming side around until they used interfaces, too.  And if it’s the same designer, then they need to smack themselves around until they make up their mind. []

October 10, 2009   No Comments

SWA: Straight Outta Redmond

Back in .Net 3.0, Microsoft slipped a little library into the Framework that was missed by most people. That library, System.Windows.Automation, was intended to allow direct programmatic access to MS UIA from .Net. UIA, or UIAutomation is Microsoft’s replacement for MSAA (Microsoft Active Accessibility), and is designed to expose window controls to accessibility devices, like screen readers for the blind. However, since it exposes all manner of window controls and operations through a direct programming interface, UIA is one of the most useful tools for UI Testers who are trying to write automation for Windows applications.1 In other words, if you want to write a program to drive the UI of another program for your automated tests, then System.Windows.Automation is where to begin.

Where to begin with SWA itself is a bit of a mystery, though. The documentation was sparse and confusing when I first started playing around with it, so most of what I know what a result of tinkering until it worked or searching the Internet until I found a similar confused person that had already solved the same problem and posted the solution. That’s why I’m writing this tutorial. I found that SWA had a learning cliff to overcome, so I hope to spare you some of the same trouble by explaining what I had to discover the hard way.

First, though, let’s take a trip through a highly opinionated aside about the general design of SWA. .Net 1.0 was beautiful and clean and easy. Everything made sense. .Net 1.1 cleaned more things up and made it even better. Then .Net 2.0 came out and the awesome was truly solidified by the introduction of generics and anonymous delegates. After that, everything fell apart. .Net 3.0 and 3.5 saw the introduction of bizarre things like WCF and Linq and semi-awesome, yet complicated things like WPF. It was like all the people who had guided the .Net Framework up through version 2 and shaped it with the mantra “Make it easy, make it clean” had been thrown out in a coup and replaced by an evil cabal of leftover COM programmers who wanted to restore the glory of MFC and ATL.

System.Windows.Automation seems to have been designed by one of these groups. At it’s core, it seems that the people who wrote it had never heard of things like interfaces or the MAUI libraries.2 When you work in SWA, you get generic AutomationElement objects, but they’re not the control type you want, and you can’t case them to the control type you want. There’s no Button class or TextBox class that you can get. Instead, you have to ask the element for the control pattern you’re interested in, and only then will you get an object that you can use. When I was first working with SWA, this approach made absolutely no sense to me. Why can’t I get an AutomationElement that I can cast to ButtonElement or IButtonElement and use directly? Why do I have to ask for these control patterns and get back some strange type? Then, about a year ago, I discovered what the model was. At that time, I was developing a toolbar for Internet Explorer, which requires extensive use of COM. This was my first exposure the the special brand of hell that is COM programming, as I mercifully had spent the late 90’s in the sheltered arena of school, and by the time I joined the real world, everyone was using .Net. When I saw QueryInterface in COM and what it was doing, it struck me that it was exactly the same thing that I’d had to do with AutomationElement.GetCurrentPattern().

The people who designed System.Windows.Automation had brought QueryInterface into the world of .Net. There is a special place in Hell for doing things like that.

Anyway, the utility of the library is enough to overcome any stupid choices in its design. So, let’s get going!

First, you’ll want to get the UISpy tool. You may already have it buried in your Visual Studio installation, but if not, head over to the MSDN and try to track it down. It’s usually part of the Windows SDK or .Net SDK, except when Microsoft apparently forgets to include it. I got mine from the Developer Tools in the Vista SDK Update, but you might want to see if there’s a better place by the time you read this.

UISpy is a lot like Spy++, which has been around since at least the VS6 days. You can walk the window control tree and find window handles and window classes and other things like that, just like Spy++, but it’s been extended with support for UIA. Once you get it, take it for a spin around your window hierarchy.3 I’d suggest turning on the “Hover” tracking mode, which lets you select a window to inspect by holding the CTRL key and hovering over a window. It’ll sometimes take a while to get to the control you’re selecting, but that’s what a full tree walk will do to you.

UISpyBasic

This screenshot shows the basic window of UISpy. On the left is the control hierarchy. On the right are the properties of the currently selected window. You’ll become very familiar with some of these properties and you’ll decide that other properties are completely useless. The determination of which fields are useful or useless is left as an exercise to the reader.

UISpyCalculatorWindow

Here’s an example of what UISpy will tell you about the window used by the Windows Calculator. On the left side, you can see that it has a bunch of child controls. They’re marked as check boxes, radio buttons, buttons, even an edit box. If the window were bigger, you’d also see that it has a title bar and menu bar. You can get information on all of these objects and interact with most of these objects. Pretty much anything you see here is something you can use SWA to control. On the right side are the properties for the selected object. Things like AutomationId, Name, and ClassName are generally good identifiers, while fields like ProcessId and RuntimeId may change from run to run.

At the bottom of the property list are the Control Patterns supported by this element. Control Patterns are how SWA interacts with controls. For instance, in this screenshot, it shows that the main calculator window supports the Transform pattern and the Window pattern. The Transform pattern means that you may be able to perform move, rotate, and resize actions on this object. In this case, the calculator reports that you can move it, but that you can’t resize or rotate it. If you right click on the element in the tree on the left side and select “Control Patterns” from the menu, you’ll get a dialog where you can trigger some of the methods on a supported control pattern. When you get to writing your automation program, you’ll ask for one of these Control Patterns and be able to use it to drive the control. There are other ControlPatterns, like “Invoke” for buttons and “Text” or “Value” for things like text boxes. You’ll probably find that you only use a small handful of these patterns regularly.

If you looked at that last screenshot of UISpy, you probably noticed that odd rectangle floating over the screen. If you’re playing the home game along with me, then you’ve probably had your own rectangle floating about. It’s the UISpy highlight window, showing the outline of the last window you selected.4 It’ll go away if you close UISpy, but I’ve found that you’ll tune it out. Sometimes I’ve had it linger around for several hours after I get the information I’ve needed, until someone comes by and asks me what that strange red thing on my screen is. If you move the mouse over the edge of the box, you’ll get a tooltip with a little bit of information on the selected window.

Anyway, we’ve been playing around in the ever-important UISpy, but we haven’t gotten around to actually using SWA yet. Giving that’s what this article is supposed to be about, let’s get to it.

My example code can be pulled from SVN here: http://www.mathpirate.net/svn/Projects/SWAExample/

I’m creating a Console App, but there’s no reason you can’t use SWA in a test DLL or a Windows app or whatever.5 It’s just a .Net library.

To begin, add references to UIAutomationClient, UIAutomationClientsideProviders and UIAutomationTypes.6

SWAReference

After that, add a using for System.Windows.Automation.

Now you’re ready to get rolling. The main class you’ll be using is the AutomationElement class. The entire control hierarchy is made up of AutomationElements. Think of them as an analog to the Control class in Windows Forms. It’s important to note that you’ll never create an AutomationElement yourself. AutomationElement does not have any constructors on it that you’re allowed to use. Instead, you’ll use methods on AutomationElements to give you other AutomationElements. The AutomationElement class has a number of static methods on it. Type “AutomationElement.” to bring up your good friend Intellisense to tell you what to do.

The first thing you’ll notice is that the AutomationElement has a hell of a lot of static fields on it. They’re pretty much all Dependency Property garbage. If you don’t know what Dependency Properties are, think of them as an enum value that will let you pass the name of a property to access on an object. (And if you do know what they are, then you know that the explanation I gave is horribly oversimplified and pretty much wrong and misleading. SHHH! Don’t tell anyone!) You can ignore them for now, but they’ll come back to haunt us in a bit. Right now, there are only four things you’ll care about on Automation Element:

  • RootElement: A reference to the root automation element, otherwise known as the main desktop window. Everything else is a child of this element. If you have to search for an element, you probably want to use this element as your base, at least until you find a better parent element to operate from.
  • FromHandle(IntPtr hwnd): If you have the window handle to the window or control you want to work with, use this method to grab an AutomationElement. It’ll be faster than searching for it and it will also give you exactly what you were looking for. I almost always start here rather than starting with a search, because you really don’t want to walk the entire control tree looking for something if you don’t have to.
  • FocusedElement: If the element you’re interested in has focus, use this and go straight there. No searching and no window handles necessary.
  • FromPoint(Point pt): Need the control at 132, 526? Use this. I’m not sure if this will do a tree walk, so use at your own risk.

To begin my example, I’m going to launch an instance of the Windows Calculator application, then use FromHandle to grab the Calculator window and print out some information on it. (BTW, I’m running XP, so if you’re playing along at home, the calculator may be different in your operating system.)

//Launches the Windows Calculator and gets the Main Window's Handle.
Process calculatorProcess = Process.Start("calc.exe");
calculatorProcess.WaitForInputIdle();
IntPtr calculatorWindowHandle = calculatorProcess.MainWindowHandle;

//Here I use a window handle to get an AutomationElement for a specific window.
AutomationElement calculatorElement = AutomationElement.FromHandle(calculatorWindowHandle);

if(calculatorElement == null)
{
throw new Exception("Uh-oh, couldn't find the calculator...");
}

//Walks some of the more interesting properties on the AutomationElement.
Console.WriteLine("--------Element");
Console.WriteLine("AutomationId: {0}", calculatorElement.Current.AutomationId);
Console.WriteLine("Name: {0}", calculatorElement.Current.Name);
Console.WriteLine("ClassName: {0}", calculatorElement.Current.ClassName);
Console.WriteLine("ControlType: {0}", calculatorElement.Current.ControlType.ProgrammaticName);
Console.WriteLine("IsEnabled: {0}", calculatorElement.Current.IsEnabled);
Console.WriteLine("IsOffscreen: {0}", calculatorElement.Current.IsOffscreen);
Console.WriteLine("ProcessId: {0}", calculatorElement.Current.ProcessId);

//Commented out because it requires another library reference. However, it's useful to see that this exists.
//Console.WriteLine("BoundingRectangle: {0}", calculatorElement.Current.BoundingRectangle);

Console.WriteLine("Supported Patterns:");
foreach (AutomationPattern supportedPattern in calculatorElement.GetSupportedPatterns())
{
Console.WriteLine("\t{0}", supportedPattern.ProgrammaticName);
}

(Apologies for the horizontal scrollies…)

The example above will output something like this, although your specific values may vary.

--------Element
AutomationId:
Name: Calculator
ClassName: SciCalc
ControlType: ControlType.Window
IsEnabled: True
IsOffscreen: False
ProcessId: 3660
Supported Patterns:
         WindowPatternIdentifiers.Pattern
         TransformPatternIdentifiers.Pattern

As you may have noticed, the information that was just printed out here is the same that was in UISpy, although the output in my example has been edited for time. Of course, if you ran this sample, you probably also noticed that the Calculator remains open after the app exits. That’s not very polite. Let’s clear that up now.

One of the patterns listed supported by the main window is, surprisingly enough, the WindowPattern. If you looked at what methods are on the WindowPattern back when you were playing around in UISpy, you may have noticed that there’s a method called Close which you can call. Something tells me that method will be useful for our current situation. I think I’m going to give it a spin.

(By the way, for my sanity and to make the examples more compact, I’m going to be moving parts of the sample code into helper functions as I go. For instance, all of those WriteLine statements have been put into a method called “PrintElementInfo”. So, if you see an odd function in the samples, that’s probably all it is. I’m not going to intentionally leave out important code that you’ll need to make things work.)

In order to get a control pattern off an AutomationElement object, you have to call QueryInt- er, I mean, you have to call GetCurrentPattern on the object. The GetCurrentPattern method takes an AutomationPattern object. AutomationPattern has a static LookupById() method on it, which is completely worthless to you, and, like everything else we’ve seen, has no public constructor. So, WTF, where are you supposed to get the pattern from? In a complete failure to make the code usable from Intellisense alone, you have to use a static member off of the type of the pattern you want to retrieve. You want to use a text box? Use TextPattern.Pattern. Need to play with a dropdown combo box? SelectionPattern.Pattern. We want the WindowPattern7, so we’re going to call GetCurrentPattern(WindowPattern.Pattern). Of course, GetCurrentPattern returns the pattern object as an ever-helpful IUnkno- I mean, object type, so you have to cast it.

Once you have the WindowPattern object, a quick examination of its members shows that it has a Close() method. Calling it should close the calculator and clean up after our program.

Here’s what those lines look like in code. Add them to the end of the sample and watch the window disappear!

//Get the WindowPattern from the window and use it to close the calculator app.
WindowPattern calculatorWindowPattern = (WindowPattern)calculatorElement.GetCurrentPattern(WindowPattern.Pattern);
calculatorWindowPattern.Close();

So, there you go! That’s all you need to know about System.Windows.Automation! You can find a window and close it, therefore, you can do anything! Have at it!

Or… Not… Let’s continue, shall we?

Since this is a calculator, let’s calculate something. Something too complex to do by hand, something we need the full power of a modern multi-core computer to figure out. Something like “What do you get if you multiply six by nine”, perhaps? To begin, you’ll need to list out the steps that you take when you manually perform this action.

  1. Open Calculator. (Hey! We did that already. We’re awesome.)
  2. Type “6”.
  3. Press the Multiplication button.
  4. Type “9”.
  5. Press the Equals button.
  6. Read the result and know the answer.

So, the first thing we need to do is type “6” into the calculator text box. So, we need to find the Calculator’s text box. Let’s bring up our friend UISpy to find out how to reference that box.

CalculatorEditBox

So, we’ve got a class name of “Edit”, a control type “ControlType.Edit” and an AutomationID of “403”. That should be enough to find the control we’re looking for, so let’s get to the code and grab it.

(BTW, obviously, this code will have to go before the Close method call we added. You can’t use a calculator that isn’t running anymore…)

An AutomationElement object has two methods that let you search the control hierarchy for the elements you’re interested in: FindAll and FindFirst. Since we’re only expecting a single text box, we’ll be using FindFirst. Intellisense will show you that FindFirst has two parameters: FindFirst(TreeScope scope, Condition condition);

TreeScope is an enum, so it’s very Intellisensible and clear. You’re like to use the values “Children” and “Descdendants” the most. Children limits the search to the immediate children of the element you’re searching on, while Descendants are the children and the children of children and so on, all the way to the bottom. I prefer to use Descendants by default, unless I know that I want something else. It should be noted that the Parent and Ancestor scopes are listed as “Not Supported”, so don’t expect to be able to use them. Anyway, we’ll use TreeScope.Descendants here.

Condition, on the other hand, offers no Intellisense help for you at all. That’s because Condition is an abstract base class of many conditions. There’s PropertyCondition, which will match based on a property value, and And/Or/Not conditions, which can be used to group multiple conditions logically. Off of the Condition class are static True and False conditions. And, if you need your own sort of crazy condition, I think you can derive from Condition and make one yourself, although I would question your MentalCondition if you were to do that without good reason. PropertyCondition is the only stock condition that you’ll find yourself using, and it’s also the only one that requires any kind of in depth explanation.

Warning! We are about to be haunted by Dependency Properties!

PropertyCondition allows you to specify the value you want a property to match for your control tree search. PropertyCondition actually has a constructor, to which you pass an AutomationProperty and a value to match. The AutomationProperty parameter is where Dependency Properties come in. You have to pass in one of the static values from the AutomationElement that I told you to ignore earlier. If you look, you’ll find that there’s one of these static values for each of the properties on an AutomationElement instance. So, if you want to find an AutomationElement that has an AutomationId of 403 (Which, coincidentally, is what we want to find), then you’ll use AutomationElement.AutomationIdProperty in your PropertyCondition. Like so:

PropertyCondition editBoxAutomationIDProperty = new PropertyCondition(AutomationElement.AutomationIdProperty, "403");

(Note that the value “403” is passed as a string. That’s because AutomationId is a string, and the types need to match. You’ll have to make sure that you’re passing the same type as the property yourself, otherwise you’ll get an exception at runtime.)

Then you pass that condition to your FindFirst call and presto, you get the element you’re looking for. (Or null or possibly an exception or maybe some other element that happens to match what you asked for or that isn’t what you wanted…) I’m going to do that now, but first, we need something to call FindFirst on. Doing a tree search can be very slow, on the order of 10+ seconds per call in some cases, so you want to limit the scope of the search. If you don’t have any element to go on, then you can use the static AutomationElement.RootElement property that I mentioned earlier, and that will look through EVERYTHING. However, we already have the main calculator window, so let’s just assume that anything in the window, including the edit box, will be a descendant of that window and use that as the starting point of our search. That gives us this:

PropertyCondition editBoxAutomationIDProperty = new PropertyCondition(AutomationElement.AutomationIdProperty, "403");
AutomationElement editBoxElement = calculatorElement.FindFirst(TreeScope.Descendants, editBoxAutomationIDProperty);

Printing out the element that’s returned gives you something like this:

--------Element
AutomationId: 403
Name:
ClassName: Edit
ControlType: ControlType.Edit
IsEnabled: True
IsOffscreen: False
ProcessId: 1884
Supported Patterns:
         ValuePatternIdentifiers.Pattern
         TextPatternIdentifiers.Pattern

Looks like the one we want, so let’s use it. We want to set the value of the control, so let’s grab the ValuePattern for the text box and use the SetValue method to set the value of the element to “6”, our first number.

ValuePattern editBoxValuePattern = (ValuePattern)editBoxElement.GetCurrentPattern(ValuePattern.Pattern);
editBoxValuePattern.SetValue("6");

Now you hit run and…

System.InvalidOperationException was unhandled
   Message="Exception of type 'System.InvalidOperationException' was thrown."
   Source="UIAutomationClient"
   StackTrace:
        at System.Windows.Automation.ValuePattern.SetValue(String value)
        at SWACalculatorExample.Program.Main(String[] args) in E:\svn\Projects\SWAExample\SWACalculatorExample\Program.cs:line 28
        at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
        at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
        at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
        at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
        at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
        at System.Threading.ThreadHelper.ThreadStart()
   InnerException:

AW CRAP IT BROKE

You followed the directions, you got an instance of the correct pattern, it should have worked, but didn’t. So what happened? Well, if you took the time to investigate the edit box in UISpy, you would have noticed this little tidbit down in the information about the Value pattern:

IsReadOnly: "True"

Lovely, so the edit box is read-only, which means we can’t assign the value in that way. Now, for a confession: I noticed that the box was read-only early on, but I still dragged you down this dead end for three reasons:

  1. I wanted to teach you that life sucks sometimes.
  2. I wanted to teach you how to use ValuePattern to set the value of a text box that’s not read-only.
  3. I wanted to illustrate one of the most important skills for an automation developer to have: The ability to come up with a Plan B workaround when the automation tool fails you. Because it will fail you. Frequently. And at the most annoying times.

The most common Plan B is to use System.Windows.Forms.SendKeys to send a series of keystrokes to do what you want. (If you’re using the VS Testing stuff, there’s also a Keyboard class that exposes essentially the same thing.) It’s less reliable than the SWA methods, so use them when you can, but when you can’t, SendKeys might just do the trick. It has a wide grammar for sending complex keystrokes and it’s well work browsing the documentation to see what it can do, but for now, we just need to type the number “6”. Focus the edit box first, using the SetFocus() method on the edit box AutomationElement. That will make the key strokes go where we want, then use SendKeys.SendWait(“6”) to simulate the keypress. (SendKeys also requires a reference to System.Windows.Forms, for don’t forget to set that up if you don’t have one already.)

//Since the direct ValuePattern method didn't work, this is Plan B, using SendKeys.
editBoxElement.SetFocus();
SendKeys.SendWait("6");

If you run it now, well, you’ll see the calculator open and close really fast, but trust me, if you watch really really carefully (or comment out the Close line), you’ll see that the edit box will have the number 6 that we just “typed”.

Now that that’s out of the way, it’s time to hit the multiplication button. You grab the button the same way you grabbed the edit box: Look in UISpy for something uniquely identifying the control, then run a tree search and get what you’re looking for. In this case, the multiply button has a meaningful name that we can use: “*”. Again, it will be a PropertyCondition, but this time, we’ll have to use the AutomationElement.NameProperty as the property to match against. Once you have the button, you’ll want to click on it. The button click action is handled by an InvokePattern, so ask the button for its InvokePattern and call Invoke on it to click the button.

//Grab the multiplication button by name, then click on it.
AutomationElement multiplyButtonElement = calculatorElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "*"));
InvokePattern multiplyButtonInvokePattern = (InvokePattern)multiplyButtonElement.GetCurrentPattern(InvokePattern.Pattern);
multiplyButtonInvokePattern.Invoke();

Now we need to enter the “9” and press the “=” button to get our answer. I’ll leave that up to you, since that’s pretty much a copy and paste of the last two things I just got done doing. You may even want to take this opportunity to refactor. You’ll find that using SWA will result in tons of areas in your code where one logical action takes three or four long lines of code, and you’ll end up with those lines copied all over the place. For instance, by parameterizing the last block of code, you can make a function called “InvokeChildButtonByName” and turn those three lines of ugly into one line that makes sense. I would strongly recommend moving as much of your SWA related code into various helper functions or classes because, quite simply, SWA code is ugly and will distract from what you’re actually trying to do.

At this point, we’re finished with the first 5 steps in performing the calculation, and are left only with the sixth and final step: Reading the result. If you recall, we’ve already seen how to pull the ValuePattern from the edit box. We couldn’t use it at the time, but that’s because it was read-only. Now that we only want to read from it, its read-onlyness shouldn’t be a problem. Once you have the pattern, in order to get the value, you’ll have to use the .Value property off the instance. However, .Value isn’t on the pattern itself. To get to it, you either have to go through the .Current or .Cached properties first. I’m not entirely sure what all of the features and limitations and differences are between .Current and .Cached, but I know that .Current usually works and that .Cached usually doesn’t, therefore I’d strongly recommend using .Current.

ValuePattern editBoxValuePattern = (ValuePattern)editBoxElement.GetCurrentPattern(ValuePattern.Pattern);
string editBoxResult = editBoxValuePattern.Current.Value;

At this point, the string editBoxResult will have the answer to the question.

Six by nine: 54.

That’s it. That’s all there is.

If you want the full source code to the example, go here.

At this point, you should know about as much about SWA as I learned in three days of fighting with the technology and documentation. Points to remember:

  • Use UISpy to inspect windows and controls to find some uniquely identifying set of information.
  • Use FromHandle, FindFirst, or FindAll to grab elements that you’re interested in using.
  • Use GetCurrentPattern to ask for an object that you can use to interact with the control in a specific way. ValuePattern and InvokePattern are two of the most common ones you’re likely to use, but become familiar with the others for when you find a different control.
  • If all else fails, use a dirty hack workaround.8

Of course, this posting only talks about how to use SWA to write automation. It completely omits the other half of the equation, which is to write a UI application that can play nicely with SWA, which will lead to happier testers. The example of using Calculator was simple. In a lot of applications, you’ll run into controls without automatable IDs, leaving you to do nasty things like grab all of the Edit controls in a window and selecting the third one from the list and hoping that it’s actually the edit box that you want. You’ll find buttons that don’t actually click, forcing you to perform crazy workarounds. It doesn’t have to be like that. When writing an application, you can implement some support for SWA, making it easier to reference the elements in your tests later. Perhaps I’ll cover that in a later post.

  1. It’s also supported by Silverlight, so all you Internet testers don’t have to feel left out. []
  2. MAUI was an internal library that was widely used for UI Automation by teams within Microsoft back when I did my time as a contractor in 2004. It was a .Net wrapper which had classes for every type of UI control. It was simple to understand and use, largely because if you wanted to interact with a button or a text box, there was a Button class and a TextBox class that you could program against. It was almost a mirror to Windows Forms. []
  3. You’ll probably find that it doesn’t work for web pages, because they’re custom drawn panels and not real Windows controls. It won’t even work for Firefox at all, because that browser is laid out entirely in XUL. I’ll probably talk about browser automation in a later article. []
  4. Which is usually the main editor window in Visual Studio, because every time you do a CTRL-C, CTRL-V, you’ll be hitting the CTRL hotkey, which makes UISpy select the window the mouse is over. []
  5. There is, however, a restriction on the environment where you can use SWA. Since it interacts with window controls and UI things, you must have a UI present for it to interact with. That means if you try to use SWA in a headless session, like the one your automated tests running on your build server will be executed in, you’re screwed. If you can, you may need to have your automated tests run on a server that’s always logged into a window session and unlocked. If not, you can do what I did, which was write a program to launch a virtual machine on an instance of Virtual Server, then remotely execute your tests within that virtual machine. Although complicated, you may actually find it easier to implement that solution than it is to get the corporate computing security policy altered so that it doesn’t get in the way of doing your job. []
  6. And why these all aren’t under one unified System.Windows.Automation library, I don’t know. Or at least named System.Windows.Automation.Client, .ClientsideProviders, and .Types. More evidence that the SWA team probably hadn’t used .Net before writing this library and that all the people keeping things sane had fled the scene. []
  7. Not to be confused with the Willow Pattern []
  8. And when that fails, then you can use fire. []

September 27, 2009   3 Comments

This is not a test. I repeat, this is not a test.

This is not a test:

[TestMethod()]
public void Test1()
{
    DataService target = new DataService(); // TODO: Initialize to an appropriate value
    DataObject data = null; // TODO: Initialize to an appropriate value
    target.DoSomething(data);
    Assert.Inconclusive("A method that does not return a value cannot be verified.");
}

It is, in fact, a complete waste of time.

I spent part of this week neck deep in unit tests that the development team has written for our latest project. At the start of this project, there was a big commitment made to have unit tests written for every component. “Test Driven Development” was tossed around in meetings, despite the fact that most of the people using it were unaware that they were using that phrase completely incorrectly.1 Anyway, I’d offered my services to help the devs develop unit tests, but, for the most part, I stayed out of the way and let them do their own thing.  This week, I decided to take a trip through what had been done.  Due to corporate confidentiality agreements, I cannot discuss what I found there, however, let’s just say it inspired this posting…2

For many developers, testing is beneath them.  They view testing as the refuge of the software engineer who couldn’t cut it as a real dev.  Testing is a simple, braindead task, requiring limited skills or intelligence.  Yet remarkably, developers who hold that view are also the ones who are invariably unable to write any kind of useful test.  Sometimes they’ll resist, claiming that “It’s not my job to write tests”.  When they reluctantly give in to the radical notion that it is, in fact, their job to write code that, you know, works, they rely on wizards or recorders to tell them what tests to write and how to write them, and frequently brag about the fact that they’ve spent all week writing unit tests, without regard to the quality of those tests. 

I have an annoucement to make for the benefit of those people:

The letters “SDE” in my title are not honorary.

Writing good test automation is every bit about software development as is, say, writing a middle-tier WCF service.  You have to think of it that way or you will fail miserably.  In normal software development, you typically have a fixed set of requirements that you base your code on.  In test automation development, you also code to a requirement:  The requirement to make sure that the software works.  That particular requirement is infinite, highly dependent on context, and generally requires actual thought to implement.3  And somehow, many developers writing tests completely lose sight of that requirement.

Back to what I started with.  We’re using Visual Studio’s Unit Tests.  One of the much touted features is the fully integrated support of unit testing in the IDE.  You can even right click on a method you’ve written and generate unit tests!  Except…  Those tests are COMPLETELY WORTHLESS.  It doesn’t actually generate a test, it creates a stub and asks you to fill out the inputs and outputs.  It fails on a basic technical level, because it produces nothing useful and typically it takes you longer to delete all the crap they’ve put in the method than it would to have simply written the test the right way from scratch in the first place.   However, more disturbingly, it fails on a philosophical level for many reasons.  Among them:

  • It enables and encourages laziness.  A few clicks and look at that, you’re a tester!  Now you don’t have to think about what you’re doing at all.  Isn’t that easy?
  • It encourages bad testing practices.  It will create a single method for a function, implying that you only have to do one thing to make sure that your code is good.  It makes it easy to produce a whole set of test stubs at once, but, oh crap, they’re all Asserting “Inconclusive” and causing the build to report errors, so better comment that out, now the build looks good and you’ve got 80 passing tests — SCORE!
  • It gives you idiotic advice, like:  “A method that does not return a value cannot be verified.”  List<T>.Clear(); doesn’t return a value, but I’m sure you can think of a way to verify that. Array.Sort(something); also has no return, and also has a clear verification.
  • It creates a worthless “ManualTests.mht” and “AuthoringTests.txt”, which are about as useful as the MFC “Readme.txt” from VS6 and should be deleted just as fast.

And there’s more, but I’ll save it for some other time, because I think you’re getting my point.  Unfortunately, because it’s Visual Studio that’s doing it, people seem to think it’s automatically the right way of doing it, and don’t bother to think about it long enough to realize that they’re doing it wrong.  Therefore, I feel it is important to provide testing advice for developers who are attempting to write unit tests.  I think I sent most of these tips to my team at the start of our project, which means that they’re still in mint, unused condition for you!  In no particular order, but numbered for your reference:

  1. Unit tests are supposed to be self-contained.  They’re not supposed to run against external DBs, they’re not supposed to hit external services.  Use your DLL directly, don’t bother going through the service that exposes your DLL.  If you can’t check it in, you can’t use it.  Remember, the purpose of a unit test is to make sure your code works, not to make sure that the database server is configured correctly.  Read up on mock data providers to see how to design your code so that you can test it without talking to a DB or external service.
  2. Automation code is the same as regular code.  You can use helper methods and classes.  You can include libraries.  If you’re doing the same thing in fifteen tests, altering only a variable or two, then do the same thing you’d do in your regular code:  Write a method that does the work and pass those variables in as parameters.  It’s the same language you normally program in, and I’m assuming you already know how to use it.
  3. Always give yourself enough information in failure messages to find out why the test failed.  xUnit4 Assert methods typically have an optional “string message” parameter so you can put a note about what you’re checking.  As far as I’m concerned, that should not be an optional parameter.  Always use it.  If you’re catching an exception, print out the message AND the stack trace.  Add status writelines if you want.  Remember, you’re probably only going to look at the details of a test case when that test case is failing or acting strange, and when the test is failing or acting strange, you want to know why.  The more details and information, the better.
  4. The default status of a test should always be failure, whenever possible.5  Any time anything goes awry, it should fail.  If you have an if-else chain or a switch that’s supposed to cover every possible case, you should have the final else or the default of the switch make the test fail when you get an impossible case.  Basically, treat the code as guilty until proven innocent.
  5. Don’t forget the easy pickings!  Can you pass nulls?  Negatives?  Boundaries?  If your method has preconditions, try inputs that fail to meet those preconditions.
  6. You should generally have more than one test per method.  If your code has an if statement in it, then that’s a minimum of two tests right there.
  7. You should generally have more than one Assert per test.  For instance, if you have a function that fills a data object and returns it to you, then you should assert that the object isn’t null and you should assert that every field in the object is the value you expect it to be.  In some cases, it’s also prudent to make sure that it didn’t do anything you weren’t expecting it to do.  In the case of a delete method, for example, it’s often a good idea to make sure the thing you wanted to get deleted was deleted AND that the method didn’t go on a killing spree and delete everything else while it was at it.
  8. You should pretty much always end up with more code in your tests than in the code being tested, or you’re doing it wrong.
  9. You should pretty much always end up with more ideas for test cases than you can ever write, or you’re doing it wrong.
  10. Don’t write a bunch of unit test stubs for things you’d like to test.  It’s worthless.  Instead of test stubs, use comments to remember what tests you’d like to write.  In normal programming, you write a stub because you want to write some other piece of code first that will need a method, but you don’t want to actually write the function just yet.  That’s not the way it is in testing.  Nothing is going to call a test except the test harness.  A stubbed out test is not only a waste of time and space, it’s also dangerous.  If you stub out a bunch of tests that you’d like to write, something is going to happen that’s going to prevent you from getting to them right away, and you’re going to end up forgetting that you’ve left all the stubs there.  Then, sometime later, you’re going to review the tests that are being run, and you’ll see 20 test cases covering method X, and from the names of the test cases, you’ll assume that method X is adequately covered.  If, for some inexplicable reason, you feel you absolutely must have test stubs, then you MUST make them explicitly fail.  That way, you’ll know that they’re there.  Also, you’ll be annoyed by the large number of failing tests, and be less likely to put stubs in next time.  It’s better to have no test at all than a bad test.  At least you know where you stand when you have no tests.  If you have bad tests, they’re lying to you and making you feel good about the state of things.
  11. Use the most specific Assert available for what you’re checking.  The more specific the Assert is, the more specific information you’ll get about when when wrong when it fails.  And if there isn’t an Assert for something that you’re checking frequently, then I recommend writing one.  They’re not magic functions or anything.  They’re typically an if statement that’ll throw an exception with a details message in certain circumstances.
  12. Avoid using try/catch blocks in your test code.  Yes, I know that sounds strange, and like it’s a bad coding practice, but you’re doing it for a reason.  First, Asserts communicate to the harness using exceptions, so if you’re catching exceptions, you risk swallowing the failure exception from an Assert.  Second, if your test is throwing an exception, you generally want to know about it and want it to fail the test.  xUnit frameworks consider any exception to be a failure, unless you’ve marked the test with an attribute telling the framework that you expect a certain type of exception to be thrown.  Now, try/finallys, on the other hand, are perfectly acceptable.  Just be careful with catches.
  13. Always make sure you’re actually testing the right code.  Don’t worry about testing the third party framework, don’t test the external class library, and don’t copy large sections of the code from your project into the test project for convenience.
  14. Don’t rely on the code you’re testing to test the code you’re testing.  Don’t call your method to make sure that your method is correct.  If your method is wrong, there’s a good chance that it will be wrong the same way both times.  Verify the code using independent means when possible.
  15. If you have an automated build setup, then integrate your unit tests such that a unit test failure causes a build failure.  Every last unit test you have should be passing at all times.  (And if you don’t have an automated build, then you need to set one up.  Seriously.)
  16. If a unit test fails, fix the code or fix the test.  You should consider a failing unit test to be the same level of seriousness as a compliation failure.  Do not ignore it and do not comment it out to make it work for now.  A unit test failure means that your code is broken.  If the test is obsolete, then delete it.
  17. Name your tests something meaningful.  Don’t call them “LoginTest1” to “LoginTestN“.  Name them something useful, like “Login_NullUsername” or “Login_ValidUsernameAndPassword” or “VerifiesThatLoginDoesNotWorkWhenUserIsNotFoundInDataStore”.  Anyone should be able to look at a test name and have some idea about what it is supposed to be testing.
  18. Test your tests.  Make sure that they’re doing what you expect them to be doing, and looking for what you expect them to be looking for.  Sometimes I’ll forcibly alter an important value, just to make sure that the test will fail.  You can even step through your test cases in a debugger and watch what’s going on, just like normal code!  (Because it is normal code!)
  19. Your test cases should never depend on one another.  Test A should never set up something for Test B, because you’re not guaranteed that Test A will run before Test B, in fact, you have no guarantee that Test A will be run at all.  xUnit frameworks typically have some sort of ClassInitialize and ClassTearDown and TestInitialize and TestTearDown methods that you can use to set up and clean up your test cases, if needed.  Read the documentation on these for your framework to be clear on exactly when these will be called.

And finally, the most important reason to be diligent about writing good unit tests:

  • If you write good unit tests, then you’re more likely to keep those annoying testers away from you, because you’re less likely to be giving them code that doesn’t work.
  1. I’m sure I’ll have something to say about TDD at some point. []
  2. To be fair, one group had their act together, producing about 480 out of the 500 or so unit tests that were there, and I didn’t see any glaring problems.  So, they’re either doing it right, or they’ve tricked me into thinking that they’re doing it right, either way, they deserve credit. []
  3. I know when a developer is starting to understand the true nature of unit testing when they cry out “Testing is hard” in anguish. []
  4. nUnit, JUnit, MSTest, MBUnit, etc. []
  5. Unfortunately, the xUnit frameworks are completely backwards in this regard and will pass any test if there isn’t an explicit failure.  This is wrong, and in my view, broken.  In the obvious case, a completely empty test method will count as a pass, even though it doesn’t do a damned thing.  In a more insidious case, an unintended code path could cause a test to pass, even though there is a bug in the system.  For instance, I recently wrote a test to verify that a method would throw an exception with a property set to a certain value.  I put the method call in a try block, my Assert on the property in the catch, and called it good.  A few days later, I realized that my test would pass if the method I was testing didn’t throw an exception at all, which was obviously incorrect. []

September 18, 2009   No Comments

Being Mean To The Average

I hate the average.

Specifically, I hate the use of the average as the predominant, sometimes only bit of information given when talking about software performance test results.  I will grant that it’s easy to understand, it’s easy to calculate, and I know that it’s one of the few mathematical concepts that can be passed along to management without much explanation.  However, it’s often misleading, presents an incomplete picture of performance, and is sometimes just plain wrong.  What’s worse is that most people don’t understand just how dangerously inaccurate it can be, and so they happily report the number and don’t understand when other things go wrong. 

Calculating the average is simple.  You take the sum of a set of numbers and divide it by the number of numbers in that set.  But what exactly does that give you?  Most people will say that you get a number in the middle of the original set or something like that.   That’s where the faith in the number begins and, more importantly, where the mistakes begin.  The average will not necessarily be a number in the middle of your original set and it won’t necessarily be anywhere near any of the numbers in your original set.  In fact, even if the average turns out to be dead-center in the middle of your data, it doesn’t tell you anything about what that data looks like.

Consider, for a moment, the annual income of someone we’ll call Mr. Lucky.  Mr. Lucky’s salary starts at $50,000.  Every year, Mr. Lucky gets a $2500 raise.  So, for five years, here’s his income:  $50000, $52500, $55000, $57500, $60000.  Over that period, his average annual income is $55000.   Great, smack in the middle.  Now, in the sixth year, Mr. Lucky wins a $300 million lottery jackpot.  What’s his average income over all six years?  Over $50 million a year.  However, it’s clearly wrong to try to claim that Mr. Lucky made $50 million dollars a year over six years, because once you look at the data, it is obvious that the $300 million is skewing the average well away from what he was actually making at the time.

Let’s take another example, one closer to home.  Every month, you get an electric bill.  Since heating and cooling are often the largest chunks of power consumption, the bill will have the average temperature for the month, in order to help you make sense of the fluctuating charges.   This year, you get a bill for $200.  Shocked, you pull out last year’s bill to compare, and discover that you paid only $50 then.  Last year, according to the bill, the average temperature was 54.3 degrees, and this year it was 53.8 degrees.    The average temperature was roughly the same, so your heating/cooling shouldn’t have changed that much.  You didn’t buy a new TV or an electric car, you turn off the lights when you’re not in the room, your shut down the computer at night, you’ve got CFL bulbs everywhere, and as far as you know, the neighbors aren’t tapped into your breaker box to power the grow op in their basement.  So…  What happened?  Let’s take a closer look at that weather…

Daily Temperature Last Year:

ElectricityBill-LastYear

Daily Temperature This Year:

ElectricityBill-ThisYear

Once you look at the actual daily temperature, it becomes clear what happened to your power bill.  Last year, the temperature was fairly constant, but this year, there were wild temperature swings.  You had your AC cranking full blast for the first part of the month, then you kept nudging up the thermostat during the end of the month.  However, since the temperature extremes offset one another, the average temperature makes it seem like both months had the same weather.

That’s the key:  Once you look at the data, it’s often clear that the average doesn’t tell the whole story, or worse, tells the wrong story.  And the problem is that people typically don’t look at the data.

Let me pull this back to the world of software performance testing and tell a story about a product I’ve worked on and how I first came to realize that typical performance testing was dead wrong.  The product was a keyword processor at the heart of a larger system.  Our customer had demanded a 1.5 second response time from our overall system, and that requirement got chipped down and used up by other components on its way to the keyword processor I was involved with.  By the time it got to us, we only had a response time cap of 250 ms in which to return our data, otherwise the larger system would give up on us and continue on its way.  So, great, I thought.  I’ll just load up the system with an ever increasing number of concurrent requests and find out how many requests we can process at the same time before our average hits 250 ms.  I did that and came up with 20 requests at once.

So we set up enough machines to handle the anticipated load with a maximum of 15 requests per box at one time, so we’d have some room to grow before we needed to add capacity.  All was well.

Until, that is, the day we launched and our keyword processor kept getting dumped on the floor by the system above us.

Something was obviously wrong.  We knew what our performance was.  At 20 concurrent requests, we had a 250 ms response time, at 15 requests, we has seen an average 200 ms response time.  We’re fast enough, and we’d proved that we were fast enough.  Statistics said so!

That right there was the problem.  We trusted the wrong information.  Sure, the average response time was 200 ms at the load we were seeing, but that said absolutely nothing about the something like 30% of the requests that were hitting the 250 ms timeout.  We frantically reran the performance tests.  The results were stunning.  While the average response time did not hit 250 ms until we reached the 20 concurrent request level, we saw a significant (and SLA violating) number of requests that took more than 250 ms by the time we reached the 10 concurrent request level.

People aren’t very happy when you tell them that a cluster size has to double…

At the time, I thought I might have just made a rookie mistake.  It was the first major system I’d done the performance testing for, and I’d had no training or mentoring.  I did what I thought was right and ended up getting burned.  Surely, I thought, everyone else knows what they’re doing.  Real performance testers using real performance testing tools will get it right.  Trouble is, I’ve since discovered that’s not the case.  Everyone else makes these mistakes and they don’t even realize that they’re making any mistakes.  And performance testing tools actively encourage testers to make these mistakes by not giving the tester the information that they really need and, in some cases, giving testers information that is just plain invalid.1

So…  What do you do about it?  For starters, don’t use the average in isolation.  Pull in some other measurement.  I like using the 95th percentile for a second opinion.2  The 95th percentile means that 95% of all requests take less than that amount of time.  That’s really more what you care about, anyway.  You’re probably not really concerned with the 5% that lie beyond that point, since they’re usually outliers or abberations in your performance anyway.  This will get rid of things like Mr. Lucky’s lottery winnings.   Additionally, you’re probably not really concerned with where the average response time lies.  People often use the results of performance testing to feed into capacity planning or SLAs.  When there are dollars on the line tied to the Five Nines, why do you care about a number that you think of as the middle of the road?  You care about the worst case, not the average case.  If we’d used the 95th or 99th percentile in our inital performance tests of that keyword processor, we would not have had the problem that we did.

But even so, the 95th percentile has its own set of issues and should also not be used in isolation.  It, too, does not tell you the complete story, and can easily hide important trends of the data.

There I go again with “the data”.  You have to look at the data.  In order to get the full picture of your system performance, you actually have to look at the full picture of your system performance.  You can’t go to single aggregate numbers here or there and call it good.

Of course, that leads to the obvious problem.  When you run a performance test, you’ll often end up running thousands upon thousands of iterations, collecting thousands upon thousands of data points.  You cannot wrap your head around that kind of data in the same way that you can see Mr. Lucky’s income or the daily chart of the temperature.

Well, not without help…

 Whenever I do performance testing now, I rely on a tool that I built that will produce several graphs from the results of a performance test run.  The simplest graph to produce is a histogram of response times.

NormalHistogram

A histogram will show you the distribution of the response times for the performance test.  At a glance, you can see the where the fastest and slowest responses lie, and get a sense for how the service behaves.  Are the response times consistent, with most of them around 150 ms, or are they spread out between 100 and 200 ms?  A histogram can also show you when things are acting strangely.  One piece of software I tested had most of the response times centered below 400 ms, but there was a secondary bump up between 500-900 ms.  That secondary bump indicated something was strange, perhaps there was a code path that took three times as long to execute that only some inputs would trigger, or there were random slowdowns due to garbage collection or page swapping or network hiccups.

AbnormalHistogram

As you can see, there are a sizeable number of responses in that bump, enough to make you want to investigate the cause.  This potential problem would have been completely invisible if you were only concerned with the average, and the full extent would not have been known if you’d been looking at the 95th percentile.3  However, it’s plainly visible that something strange is going on when you see the graph like that.

While helpful, simple histograms like this are not enough.  In particular, they fall down if anything changes during the test run.  If something like network latency slows down your test for a brief period of time, that will be invisible in this graph.  If you’re changing the number of users or the number of concurrent requests, then the times from all of them get squeezed together, rendering the graph invalid.  What you’re missing here is the time dimension.

One way to bring in the time dimension is to animate the histogram.  You produce multiple histograms, each representing a slice of time in your test.  That way, you can watch the behavior of your service change over the length of the performance test.  The problem I have with an animated histogram is that you can’t just glance at the information.  You have to watch the whole thing, which can be time consuming for a long running performance test.

Instead of animation, I prefer to visualize the time in this way:

 NormalDensityGraph

Going up the side, you have divisions for buckets of response times.  Going across, you have time slices.4   Essentially, this is what you’d see if you stacked a bunch of histograms together side by side, then looked at them from the top.  It’s basically like a heat map or a graph of the density of the response times.  The red zones have the most responses, while the green areas have the least.5  In the example above, you can see that there are a lot of responses in the 100 and 150 ms buckets and then it quickly trails off to zero.  There’s a lot of noise up to about 600 ms, and sporadic outliers above that.  The performance remains fairly stable throughout the test run.  All in all, this is a fairly standard graph for a well behaved piece of software.6

These density graphs aren’t terribly interesting when things are well behaved, though.  Here’s another graph I saw:

AbnormalDensityGraph1

First, there’s bands of slowness between about 210 and 280 and 380-450.  These bands would appear in a histogram like the secondary hill shown above.  But what a histogram isn’t going to show you is the apparent pattern in the 380-450 band.  It appears that there’s groups of slow responses in a slice of time, then none for the next couple of slices, then another group of slow responses, then none, and so on.  Seeing this kind of behavior can help you find the problem faster.  In this case, the slow responses may be caused by something else running on the box that’s scheduled to run at a regular interval, like an anti-virus scanner or a file indexer, or they can be caused by something like the garbage collector waking up to do a sweep on a somewhat regular basis.

Another benefit of a density graph is that they’re still useful, even if you change the parameters of the test during the test run.  For instance, a common practice in performance testing is to increase the load on a system during the run, in order to see how performance changes.

AbnormalDensityGraph2

In this example, the number of concurrent users was steadily increased over the run of the test.  In the first part of the test, you can see that increasing the number of users will directly influence the response time.  It also increases the variation of those response times:  For the first part, all of the responses came in within 100 ms of one another, but pretty quickly, they’re spread over a 300-400 ms range.  And then, during the final third of the test, everything went all kerflooey.  I don’t know what happened, exactly, but I know that something bad happened there.

I think this graph is one of my favorites:

AbnormalDensityGraph3

As you can see, this graph is distinctly trimodal.  There’s a steep, but well defined band shooting off the top, then a wide and expanding band in the middle, followed by a sharply narrow band with a shallow slope.  I like this graph because it doesn’t actually show anything that’s wrong.  What it illustrates is the huge impact that your test inputs can have on the results.  This test was run against a keyword index system.  The input I used was a bunch of random words or phrases.  Different words caused the keyword index system to do different things.  The shallow band at the bottom was created by the keywords for which the index system found no results.  When it didn’t find anything, the system simply returned immediately, making it very fast.  The middle band was filled with keywords that found a single result.  The top band was the set of keywords that found multiple results, which required extra processing to combine the two results into one before returning.

 

Performance tests are the same as any other test, your goal is to find problems with the software.  You’re not going to find them if you’re only looking at the average.  So, the next time you’re involved with a performance test, remember: Look deeper.  There are more problems to be found under the surface.

  1. I’ve seen several cases of odd numbers coming out of VS Perf Tests, but the one I’m specifically thinking of here is the fact that VS will still report an average response time, even when you’re ramping up the number of users.  The performance of your system when you have a single user is vastly different than when you have 100 users, so the single “Average Request Time” number that it will report is just plain useless. []
  2. You can get the 95th percentile in Visual Studio, if you tweak a setting.  Set “Timing Details Storage” to “Statistics Only” or “All Individual Details” and it’ll start being recorded. []
  3. Likely, the 95th percentile would lie in the middle of the bump, leading you to believe that the system performance is slow overall, not that there’s an anomaly. []
  4. Time isn’t marked in these examples because the tool I use to generate them will usually generate other graphs, as well, making it possible to correlate the time on that graph with the time on this graph.  For these examples, it’s not really necessary to know how much time each column is or how many requests are represented by each block. []
  5. And the white zones are for loading and unloading only.  There is no parking in the white zones. []
  6. For comparison, this graph is from the same test run that produced the “Average Response Time: 149 ms” histogram that I showed earlier. []

September 13, 2009   No Comments