Tuesday, July 8, 2008

Simulate user's input

We have learned in the previous part how to explore the tested application and read its values. To test it, we also need set values to its fields.

The first idea may be to utilize the .NET objects. When we can read values from the tested application fields, we can also set them. Use the code snippet from the previous part to run the tested application and follow it by command
App.Controls[2].Text = 'GUIATtext'
The tested application is started and GUIATtext appears in the New listbox item text box.

Well, it does not appear as a proper testing to me. I'd rather simulate user's interaction with mouse and keyboard. Unfortunately, sending key strokes and mouse events is not job for .NET framework. We have to dive deep into Windows and use Win32 API to perform these tasks.

I have created a simple DLL to make Win32 API functions available for IronPython [1]. Moreover, I have added functions to simulate user's input. Here is the list of functions in the Win32API namespace with a short description:
GetForegroundWindow()
        return handle of the active window
SetForegroundWindow(handle)
        set the active window to the window with handle
        return boolean
ShowWindow(handle, state)
        set window with handle to state (minimalized, maximalized, ...)
        return boolean
GetWindowText(handle, title, capacity)
        return title of window with handle, title variable type is String of capacity size
MouseClick(X, Y)
        simulate click with left mouse button on position X, Y
SendKey(virtual_key)
        simulate pressing virtual_key
SendString(string)
        simulate typing a string

I will not go into details here. Anyone interested can explore the sources of the Win32API.dll.

Using these functions is straightforward in IronPython. Just download the Win32API.dll and look at the example below how to perform a mouse click on the specific position:
import clr
clr.AddReference('Win32API')
from Win32API import Win32API
Win32API.MouseClick(10, 10)
The code snippet moves mouse to the coordinates 10, 10 (that is to the top-left corner of the screen) and perform a click with the left mouse button. More useful examples will follow in next parts.

Finally, couple of warnings. The simulated mouse click is caught by the top most window on the position. And the simulated key strokes are sent to the active window. That means we have to pay attention to position and active state of the tested application.

In the next part, we start building the GUIAT testing framework.

[1] I do not know how to access Win32 API directly from IronPython.

Tuesday, June 24, 2008

Exploring test application: IronPython (2)

We demonstrated how to run a .NET application from IronPython in the previous part. However, we couldn't control it while it was running. The solution for this problem is to run the tested application in a separate thread. Thus we will be able to enter commands in IronPython console while the tested application is running. Here is a snippet from the source:
import clr
clr.AddReference('System')
clr.AddReference("System.Windows.Forms")
from System import *
from System.Reflection import *
from System.Threading import *
from System.Windows.Forms import Application
from time import sleep

def RunMeCallBack(var):
global App
asm = Assembly.LoadFrom('GUIAT_PoC.exe')
asm_type = asm.GetType('GUIAT_PoC.frmGUIAT')
App = Activator.CreateInstance(asm_type)
Application.Run(App)

App = None
ThreadPool.QueueUserWorkItem(WaitCallback(RunMeCallBack))
while not App:
sleep(0.2)
The RunMeCallBack function starts the tested application the same way as we showed in the previous part. We create an independent thread and run this function in it so it does not block the console. The thread finishes its work when the tested application terminates (the main form of the application is closed) or when the console is closed.

The important part is line
App = Activator.CreateInstance(asm_type)
Here we remember the instance of the main form in the variable App. We have access to the whole application thanks to the App variable! The while cycle at the end ensures waiting until the App variable is not None. Which only happens when our tested application is up and running.

The App variable is our Holy Grail. Let's explore what is inside:
>>> App
<GUIAT_PoC.frmGUIAT object at 0x000000000000002B
[GUIAT_PoC.frmGUIAT, Text: GUIAT - Proof of Concept]>
>>> App.Text
'GUIAT - Proof of Concept'
Basically, we have access to all public properties and methods. Try dir(App) and you'll see. With a trick, we can even access private properties and methods (using the power of reflection).

To find what components are on the main form, iterate through the Controls collection:
>>> for c in App.Controls:
... print c.Name, c.GetType()
...
btnAddItem System.Windows.Forms.Button
lblNewItem System.Windows.Forms.Label
txtNewItem System.Windows.Forms.TextBox
lbxItems System.Windows.Forms.ListBox
btnQuit System.Windows.Forms.Button
To find out what text is in the text box, try the following:
>>> App.Controls[2].Text
''
Now, write something directly into the text box in the tested application and call the statement again:
>>> App.Controls[2].Text
'something'
Cool, isn't it? ;-)

Next time, I show you how to simulate user interaction programatically - how to send a text or click to the tested application.

Sunday, June 22, 2008

Exploring test application: IronPython (1)

How can IronPython help with exploring the test application? The answer is .NET Reflection:

Reflection is the mechanism of discovering information solely at run time.

We can discover both class and instance information. That's important. Knowing class information, we know what the object is capable of. We know whether it is a button having click method or a text edit having value property. And knowing instance information, we can find out what text the text edit displays.

That's nice but to be able to utilize the reflection, we need access to the tested application's objects from our testing framework. That's easy. The tested application is a .NET application so we can run it directly from IronPython.

When you look to Program.cs (source), you see how the tested application is started. If you don't have the sources of tested application available, you can use Lutz Roeder's .NET Reflector to find the information.
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmGUIAT());
}
Let's mimic the start in IronPython. We are not interested in the first two rows. The third row is the core. The Application class is part of System.Windows.Forms namespace. So we just need to create an instance of frmGUIAT class. To create it, we use method CreateInstance of Activator class from System namespace. The CreateInstance method needs to know the type of the future instance. We find the type utilizing the reflection.

The whole code looks like this:
import clr
clr.AddReference('System')
clr.AddReference("System.Windows.Forms")
from System import *
from System.Reflection import *
from System.Windows.Forms import Application

asm = Assembly.LoadFrom('GUIAT_PoC.exe')
asm_type = asm.GetType('GUIAT_PoC.frmGUIAT')
App = Activator.CreateInstance(asm_type)
Application.Run(App)
When you run the above code from IronPython console, the tested application is displayed. Note the IronPython must be started form the directiory where GUIAT_PoC.exe is located. The disadvantage is we cannot do anything in IronPython console while the tested application is running. The solution is to run it in separate thread. I will show it in the next article.