
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2069 fd906abe-77d9-0310-91a1-e0d9ade77398
343 lines
13 KiB
HTML
343 lines
13 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
|
<html>
|
|
<head>
|
|
<title>Windows Application Test System Using Python</title>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
<link href="../tac.css" rel="stylesheet" type="text/css">
|
|
</head>
|
|
|
|
<body>
|
|
<img src="../images/tizmoi.jpg">
|
|
<H2>WATSUP - Windows Application Test System Using Python</H2>
|
|
|
|
The WATSUP toolkit is designed to allow the automated test of Windows applications.
|
|
The system uses the "object-based" mechanism for identifying and invoking actions
|
|
on controls and menu items.
|
|
<P>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!</P>
|
|
|
|
<p>The examples in this document assume a basic familiarity with Python.</p>
|
|
|
|
|
|
|
|
<H2>Functional Tests</H2>
|
|
|
|
Testers/developers write automated functional tests which follow a prescriptive,
|
|
possibly branching, possibly dynamic "user workflow". The script can check for
|
|
changes in the gui itself, operating system environment, file system, database
|
|
table and records, network, internet or extranet urls/pages/web services ... - in
|
|
fact anywhere that there could be changes.
|
|
<p>
|
|
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:
|
|
<ul>
|
|
<li>Get and set text in editable controls</li>
|
|
<li>Edit and select items from controls supporting lists</li>
|
|
<li>Click and double-click controls to invoke their actions</li>
|
|
<li>Determine the state of menu items and invoke them</li>
|
|
</ul>
|
|
<p> 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) .</p>
|
|
|
|
<H3>Example 1 - automated writing on Notepad</H3>
|
|
<p>Here's a simple example of the control over applications that you can have with watsup.
|
|
First, launch notepad from:</p> <p>Windows Start Menu - All Programs - Accessories - Notepad</p>
|
|
Then run the following script (<a href="code/example1.py">Example 1</a>)
|
|
<code>
|
|
<pre>
|
|
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)
|
|
</pre></code>
|
|
Finally, close notepad.<p></p>
|
|
|
|
<h3>Example 2 - testing a simple example </h3>
|
|
|
|
In functional tests, the tester wants to ensure that
|
|
the cause - invoking a sequence of windows events (button clicks, menu item activation)
|
|
has the predicted effect of, for example, a change in a value of a sindows control,
|
|
the creation of a file, or the entry of a new database record.
|
|
See the directory watsup/examples/simple directory for the executable simple.exe.
|
|
|
|
If you run the application, you see a button and a text box.
|
|
Enter a valid filename into the box, say xyz, and
|
|
click the button;
|
|
after the file is created, a message box appears containing a success message,
|
|
and investigation of the directory watsup/examples/simple will show a file
|
|
called 'xyz.txt' has been created (or overwritten).
|
|
|
|
<p>Now let's script a test to automate this functionality. </p>
|
|
|
|
First find and launch the application.
|
|
Then run the following script (<a href="code/example2.py">Example 2</a>)
|
|
<code><pre>
|
|
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()
|
|
|
|
|
|
</pre>
|
|
</code>
|
|
|
|
|
|
<h3>Example 3 - automating program launch and termination</h3>
|
|
|
|
<p>It's a bit tedious having to start and close the application each time.
|
|
<a href="code/example3.py">Example 3</a> launches the application,
|
|
if it isn't already running, and terminates it on
|
|
completion of the test</p>
|
|
|
|
<code><pre>
|
|
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)
|
|
|
|
</pre></code>
|
|
|
|
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.
|
|
|
|
<H3>Example 4 - finding windows and controls</H3>
|
|
|
|
<p>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.</p>
|
|
|
|
<p>In the tools directory, there's a tool - ShowWindows.bat - to assist us with this.</p>
|
|
<img src="images/ShowWindows1.jpg" alt="Show Windows 1" />
|
|
|
|
|
|
<p>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. </p>
|
|
|
|
<img src="images/ShowWindows2.jpg" alt="Show Windows 2" />
|
|
|
|
|
|
<H2>Performance Tests</H2>
|
|
|
|
<p>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").</p>
|
|
|
|
<p>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.</p>
|
|
|
|
<p>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.
|
|
</p>
|
|
|
|
|
|
<p>So we have <a href="code/example4a.py">Example 4a</a></p>
|
|
<code><pre>
|
|
from example4 import main
|
|
|
|
main('perform.exe','Performance Form 1')
|
|
|
|
</pre></code>
|
|
<p>and <a href="code/example4b.py">Example 4b</a></p>
|
|
<code><pre>
|
|
from example4 import main
|
|
|
|
main('perform2.exe','Performance Form 2')
|
|
|
|
</pre></code>
|
|
|
|
<p>which reference <a href="code/example4.py">Example 4</a>:</p>
|
|
<code><pre>
|
|
|
|
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'
|
|
|
|
</pre></code>
|
|
|
|
<h4>Key points in example4.py</h4>
|
|
|
|
<p>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:</p>
|
|
|
|
<p>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.</p>
|
|
|
|
<p>So in example4.py, we see that the check method requires a 1 second acceptance.
|
|
Hence example4a succeeds and example4b fails.
|
|
</p>
|
|
|
|
<H2>Regression Testing</H2>
|
|
|
|
<p>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. </p>
|
|
<p>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:
|
|
</p>
|
|
<img src="images/framework1.jpg">
|
|
|
|
<p>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</p>
|
|
|
|
<img src="images/framework2.jpg">
|
|
|
|
<p>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</p>
|
|
|
|
<img src="images/framework3.jpg">
|
|
<p>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.
|
|
|
|
<H2>Downloads</H2>
|
|
|
|
Download <a href="../downloads/watsup-0.4.zip">watsup</a> here
|
|
<p>(This package should be unzipped in the site-packages directory in your python installation)</p>
|
|
|
|
<H3> Dependencies</H3>
|
|
<ul>
|
|
<li><a href="http://www.python.org/download"> Python</a> (version at least 2.3)</li>
|
|
<li><a href="http://sourceforge.net/projects/pywin32">pywin32 </a></li>
|
|
<li><a href="http://sourceforge.net/project/showfiles.php?group_id=71702">ctypes </a></li>
|
|
<li><a href="http://www.rutherfurd.net/python/sendkeys/#binaries" >SendKeys </a></li>
|
|
<li><a href="http://www.wxpython.org/download.php" >wxPython library</a> (for tools)</li>
|
|
</ul>
|
|
|
|
<h3>Credits</h3>
|
|
|
|
<p> 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
|
|
<a href="http://www.brunningonline.net/simon/python/index.html">Simon Brunning</a>,
|
|
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.
|
|
</p>
|
|
|
|
Dr Tim Couper<br/>
|
|
<a href="mailto:timc@tizmoi.net">timc@tizmoi.net</a>
|
|
|
|
</body>
|
|
</html>
|