andrewducker (
andrewducker) wrote2008-11-29 09:39 pm
I Win at Geekiness!
I just solved a problem that's been killing me for two days at work.
I've been putting together automated Acceptance Tests at work, using Fitnesse to allow our business analysts to specify tests that are then run against the code on a daily basis before it's released to the system testers.
The problem being that they had some tests that didn't work - they wanted to check that only certain input was accepted in specified textboxes, that maximum lengths were set, etc. However, Textbox.MaxLength only affects keyboard input, it doesn't affect the .Text property if it's set directly.
So I spent Thursday and Friday trying to find a way of simulating keyboard input. Turns out that doing this is really, really easy if you can guarantee that the form you're testing is the top one on the desktop - i.e. the place where keyboard input would normally go. Problem being that I could guarantee that.
Ok - next step, grab a handle to the control I want to talk to and send it messages using PostMessage. Which I played with and got working in test code, and then couldn't get working in my actual tests. I realised at 5:30 last night that it wasn't going to work because I wasn't actually running a Message Loop - all of the forms are being launched via the TestRunner exe - and they don't work the same way they would if they had a windows message pump running in the background (if this makes no sense to you then this might help - or this for a longer explanation).
And then it hit me this afternoon - every control has a WndProc method in it (so that you can override it for custom message processing) - and I could call that directly rather than relying on the Message Loop to pass it to the correct place.
Fortunately I have Visual Studio at home, so I fired it up, played around for half an hour, and with a bit of reflection (because WndProc is "protected" rather than "public") I was happily passing messages to it.
I hate having unsolved problems - and I'm remarkably happy that I got this one done!
I've been putting together automated Acceptance Tests at work, using Fitnesse to allow our business analysts to specify tests that are then run against the code on a daily basis before it's released to the system testers.
The problem being that they had some tests that didn't work - they wanted to check that only certain input was accepted in specified textboxes, that maximum lengths were set, etc. However, Textbox.MaxLength only affects keyboard input, it doesn't affect the .Text property if it's set directly.
So I spent Thursday and Friday trying to find a way of simulating keyboard input. Turns out that doing this is really, really easy if you can guarantee that the form you're testing is the top one on the desktop - i.e. the place where keyboard input would normally go. Problem being that I could guarantee that.
Ok - next step, grab a handle to the control I want to talk to and send it messages using PostMessage. Which I played with and got working in test code, and then couldn't get working in my actual tests. I realised at 5:30 last night that it wasn't going to work because I wasn't actually running a Message Loop - all of the forms are being launched via the TestRunner exe - and they don't work the same way they would if they had a windows message pump running in the background (if this makes no sense to you then this might help - or this for a longer explanation).
And then it hit me this afternoon - every control has a WndProc method in it (so that you can override it for custom message processing) - and I could call that directly rather than relying on the Message Loop to pass it to the correct place.
Fortunately I have Visual Studio at home, so I fired it up, played around for half an hour, and with a bit of reflection (because WndProc is "protected" rather than "public") I was happily passing messages to it.
I hate having unsolved problems - and I'm remarkably happy that I got this one done!
static class TextSender
{
private const int WM_CHAR = 0x0102;
public static void SendKeys(string text, Control control)
{
MethodInfo method = typeof(Control).GetMethod("WndProc", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (Char myChar in text.ToCharArray())
{
Message message = new Message();
message.HWnd = control.Handle;
message.Msg = WM_CHAR;
message.WParam = (IntPtr)myChar;
method.Invoke(control, new object[] { message });
}
}
}
no subject
To clarify: I mean on all TextBoxes or on some TextBox instances. I don't mean looking for places where you do someinstance.Text=... and replacing it with if ....length > foo then ...
no subject
To clarify: There is code in the system that does all of this. We;re using built in MS controls that support maximum length validation, etc. They just do their validation when keys are pressed - which means that we need to simulate that.
no subject
How are you finding Fitnesse? We could really do with some kind of gui testing automation...
no subject
And I just realised I don't need the struct definition in that code any more because it's not calling PostMessage any more. Huzzah for being able to simplify it more when I get into work on Monday!
It took me a week to get the hang of Fitnesse, and I'm sure there are a large number of things still out there to trip me up, but it's getting there, and with a bit of infrastructure code in place it's actually working really well. We had to pull a fair bit of our code apart so that we could fake things in it - you can't have modal dialog boxes popping up, because then control never returns to the test, so instead we have an IDialogLauncher, with the "real" one launching things modally, and the "test" one launching things non-modally. But once that was in place it's largely going pretty easily.
no subject
no subject