So much has been written about the scope, robustness, scalability and outstanding usability of Python that I'll go no further with it here, only to say that if you haven't yet had a chance to use this comprehensive, open source language, don't miss out on the opportunity to take a serious look at it!
The examples in this document assume a basic familiarity with Python.
Examination of the functions in module autoWinGui.py within the watsup package shows the variety of windows control items that can be checked/modified. These include:
The system also provides tools for finding windows by caption and/or class, controls by text/caption and/or class, and menu items by text or position. (One of the aspirations of this project is to continue to extend the list to include as many controls as possible) .
Here's a simple example of the control over applications that you can have with watsup. First, launch notepad from:
Windows Start Menu - All Programs - Accessories - Notepad
Then run the following script (Example 1)
from watsup.winGuiAuto import findTopWindow,findControl,setEditText
from time import sleep
# Locate notepad's edit area, and enter various bits of text.
notepadWindow = findTopWindow(wantedClass='Notepad')
editArea = findControl(notepadWindow,wantedClass="Edit")
setEditText(editArea, "Hello, again!")
sleep(0.8)
setEditText(editArea, " You still there?",True)
Finally, close notepad.
Now let's script a test to automate this functionality.
First find and launch the application. Then run the following script (Example 2)
from watsup.winGuiAuto import findControl,setEditText, findTopWindow,clickButton
import os
import os.path
FILENAME='atestfile.txt'
def main():
# delete any occurrence of this file from the disk
if os.path.exists(FILENAME):
os.remove(FILENAME)
form=findTopWindow(wantedText='Simple Form')
button=findControl(form,wantedText='Create file')
editbox=findControl(form,wantedClass='TEdit')
# enter a filename:
setEditText(editbox,[FILENAME])
print 'clicking button to create file'
clickButton(button)
# now check that the file is there
if os.path.exists(FILENAME):
print 'file %s is present' %FILENAME
else:
print "file %s isn't there" % FILENAME
if __name__=='__main__':
main()
It's a bit tedious having to start and close the application each time. Example 3 launches the application, if it isn't already running, and terminates it on completion of the test
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows
import example2
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText='Simple Form')
if forms:
form=forms[0]
else:
form=launchApp('simple.exe',wantedText='Simple Form')
example2.main()
# and terminate the form
terminateApp(form)
launchApp starts the application in a separate thread,
and looks for a window with caption containing "Simple Form",
returning the window handle of the recovered form.
terminateApp attempts to close the form, by trying to activate menu item File-Exit, or, failing that,
sending Alt + F4.
In building scripts, we need to be able to find the class and/or text of the many windows and controls to be investigated or invoked.
In the tools directory, there's a tool - ShowWindows.bat - to assist us with this.
Clicking the "Register" button persists information about the existing windows running on the system (and it tells you how many, FYI). Clicking the "Find new" button will report all non-trivial windows and all their constituent controls which have appeared in the windows environment since the last "Register" click. So to test our program simple.exe, launch ShowWindows, click Register. Then launch simple.exe and and click the Find New button. The associated text box shows n/m, where m is the total number of new windows found, and n is the number of those which are significant (ie have any controls) and are reported.
Performance tests, in this definition, are single-client scripts, which are similar in operation to the functional tests above, but for which certain steps of the tests must either be done within an acceptable timeframe ("CHECKING") and/or the time taken for those steps should be recorded for subsequent analysis ("RECORDING").
WATSUP provides a simple mechanism to add such tests to existing functional tests. In examples 4a & 4b, we launch almost identical applications, perform.exe and perform2.exe respectively. When the button is clicked, the text "Finished" is written to the edit box. The difference between the programs is that the former is coded to wait for 1/2 second before "Finished" appears; in the latter case, the delay is 1.5 seconds.
In both cases, we are setting a performance test that the process take no more than 1 second. Clearly, we should expect example 4a to be ok and example 4b to fail.
So we have Example 4a
from example4 import main
main('perform.exe','Performance Form 1')
and Example 4b
from example4 import main
main('perform2.exe','Performance Form 2')
which reference Example 4:
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows, findControl,getEditText,clickButton
from watsup.performance import PerformanceCheck,PerformanceCheckError
from time import sleep,time
def main(myExecutable,myWantedText):
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText=myWantedText)
if forms:
form=forms[0]
else:
form=launchApp(myExecutable,wantedText=myWantedText)
button=findControl(form,wantedText='Click me')
editbox=findControl(form,wantedClass='TEdit')
#start a performance check instance
p=PerformanceCheck()
clickButton(button)
# belts and braces to avoid infinite waiting!
maxWaitTime=2.0
startTime=time()
while time()-startTime<maxWaitTime:
t=getEditText(editbox)
if t:
break
else:
sleep(0.1)
else:
raise Exception,'Failed to get value after maxWaitTime of %s secs' % maxWaitTime
try:
try:
#do the check/recording step, identifying this step with the wantedtext
p.check(myWantedText,1.0)
except PerformanceCheckError,e:
print '** Failed: %s' % e
# and terminate the form
finally:
terminateApp(form)
if __name__=='__main__':
print ' please run example4a or 4b'
Immediately prior to clicking the button, we establish a PerformanceCheck instance, which, among other things, establises a timer. Every time the check() method is called on a PerformanceCheck instance, the following occurs:
Also if we are CHECKING (the default condition), that time is checked against the number of seconds added as the second parameter. If the elapsed time exceeds the value of the 2nd parameter, an exception is raised.
So in example4.py, we see that the check method requires a 1 second acceptance. Hence example4a succeeds and example4b fails.
Functional and performance scripts such as those above are immediately useable within a test framework - the excellent python unit test framework is highly recommended (unittest.py, which comes along with your python installation). This then enables the tester to develop complete regression tests involving any combination of Functional & Performance testing.
For an example of the use of functional and performance tests within the unit test framework, run the program framework.bat in the tools subdirectory. This will launch a nice user interface from which sets of test cases can be run:
Select the File menu option, and you have the option to load files which contain testcases, or directories/directory trees. If select the "Load files" option, and navigate up one directory, and then down through examples and the unittests directories, you should find, and select, exampleTests.py. The Framework program examines the selected file(s) and extracts the TestCases, presents in the top frame
Selecting Menu option Testing - Run Tests (or the long button labelled Run Tests), will cause each test to be run; success or failure is shown in the lower frame
For more information on python's unit test module, refer to the python documentation. Finally, it is worth noting that this framework will work with any unit tests, not just those specifically testing windows application, so you can test elements of the logic that you have written in other applications.
(This package should be unzipped in the site-packages directory in your python installation)
The framework test tool was built from parts in unittestgui.py by Chris Liechti. Much of the core functionality in WATSUP derives from the important work by Simon Brunning, who, in his module winGuiAuto.py provided concise, accessible mechanisms to access and "click" windows controls & menus; Simon's work recognises the huge contribution that Mark Hammond has made to the python community (and wider) in providing pywin32, comprehensive win32 API modules for python.
Dr Tim Couper