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>
 |