Replay Squish tests using the ‘squishtest’ module

Replay Squish tests using the ‘squishtest’ module

Why?

Since 5.1, Squish offers a squishtest Python module. It lets you use Squish as a module in Python scripts without involving the squishrunner.

If using the squishtest module helps you improve your testing efforts but you already have a lot of existing testcases (written in Python), this might be a solution for you. Because the module works a little different, you would have to adapt every script manually. That can be a lot of work. You also might want to keep recording and editing your test scripts using the Squish IDE.

Thankfully, with a little Python magic, you can keep your original test scripts but run them with only Python and the module. The main task here is to create an environment in which the test script can be executed as before without modifications. For this we will create an extra Python script that does that and then calls the original script. Let’s see how to do this step by step:

How…

First, you should follow this tutorial to set up your Python environment to be able to use the squishtest module.

Now, create a Python script and start it off by including the needed modules:

import squishtest
import sys, os

To stay organized, lets define the path to our test suite:

suite_path = '<PATH TO TEST SUITE FOLDER>'

If you are using the Scripted Object Map (introduced with Squish 6.4), add the shared scripts folder of your test suite and the Squish Python library path to the environment:

squish_prefix = os.path.split( os.path.dirname( squishtest.__file__ ) )[0]
sys.path.append( os.path.join( squish_prefix, 'lib/python' ) )
sys.path.append( os.path.join( suite_path, 'shared/scripts' ) )

Otherwise we load the object map manually. It’s generally located in your test suites root folder. (Note that the loaded object map is read-only and changes will not persist beyond execution.)

squishtest.objectMap.load( os.path.join( suite_path, 'objects.map' ) )

Next we will load the actual test script we want to execute. Define the file path to your test script first:

test_filepath = os.path.join( suite_path, '<TEST CASE>/test.py' )

To not bloat the environment too much and avoid clashes with standard Python modules, let’s use Python’s import functionality to import the file directly. (These vary a lot between different Python versions, just pick the right one for you.)

Python 2:

from imp import load_source
test = load_source( 'test', test_filepath )

Python 3.3 or 3.4:

from importlib.machinery import SourceFileLoader
test = SourceFileLoader( 'test', test_filepath ).load_module()

Python 3.5 or higher:

import importlib.util
spec = importlib.util.spec_from_file_location( 'test', test_filepath )
test = importlib.util.module_from_spec( spec )
spec.loader.exec_module( test )

 

Next up comes the Python “magic” mentioned in the beginning. Just follow the code example for now, it will be explained why we need to do this in a moment.

Because methods are first-class values in Python, we can store and write them like any other value. Let’s use this to overwrite the standard startApplication( autName ) method.

_startApplication = squishtest.startApplication
def startApplication_updateToolkit( autName ):
    global test
    _startApplication( autName )
    test.__dict__.update( squishtest.__dict__ )
squishtest.startApplication = startApplication_updateToolkit

 

So, why are we doing this?

In Squish, toolkit-specific methods are only loaded after the AUT has been started. The squishtest module offers these after the start. Because we can’t re-import squishtest in the test script itself, we overwrite startApplication() to do that for us.
When importing modules in Python, only the script in which they are imported has access to their functionality. This means that the imported modules can’t “see” each other. The test script we are importing does not automatically have access to the functions of the squishtest module, unless we tell it. Python lets us change namespaces (which are basically just global dictionaries), thus calling test.__dict__.update( squishtest.__dict__ ) updates the namespace of our test script to contain what is imported in squishtest.

 

Of course we have to update the test scripts namespace once before calling startApplication():

test.__dict__.update( squishtest.__dict__ )

What’s left are mandatory method calls to use the squishtest module.

squishtest.setTestResult( '<FORMAT>', '<DESTINATION>' )
squishtest.testSettings.setWrappersForApplication( '<AUT>', '<TOOLKIT>' )

(Note: The AUT and the toolkit specified in setWrappersForApplication() have to match those that are being used in the actual test script.)

 

Now we can call the main() method of our test script and it should execute as usual!

test.main()

 

Example

Let’s say we have a minimal test script (using the Squish addressbook example) with the following configuration:

Squish: Squish for Qt 6.4 (using Scripted Object Map)
Python: Python 2.7

import names

def main():
    startApplication( 'addressbook' )
    clickButton( waitForObject( names.address_Book_New_QToolButton ) )

(Note that clickButton() is a toolkit-specific method.)

Then our script to execute this with the squishtest module could look like this:

import squishtest
import sys, os

suite_path = '/home/user/suites/addr'

squish_prefix = os.path.split( os.path.dirname( squishtest.__file__ ) )[0]
sys.path.append( os.path.join( squish_prefix, 'lib/python' ) )
sys.path.append( os.path.join( suite_path, 'shared/scripts' ) )

test_filepath = os.path.join( suite_path, 'tst_minimal/test.py' )

from imp import load_source
test = load_source( 'test', test_filepath )

_startApplication = squishtest.startApplication
def startApplication_updateToolkit( autName ):
    global test
    _startApplication( autName )
    test.__dict__.update( squishtest.__dict__ )
squishtest.startApplication = startApplication_updateToolkit

test.__dict__.update( squishtest.__dict__ )

squishtest.setTestResult( 'xml2', '/home/user/results/addr/results.xml' )
squishtest.testSettings.setWrappersForApplication( 'addressbook', 'Qt' )

test.main()

 

Additional Notes

Depending on how you start your AUT, just overriding startApplication( autName ) might not work. Watch out for the parameter signature of the method you want to override as well (e.g.: startApplication( autName ) vs. startApplication( autName, host, port, timeoutSecs )).

If you intend to change the application context during your tests, make sure all AUTs have toolkit wrappers set for them beforehand with:

squishtest.testSettings.setWrappersForApplication( '<AUT>', '<TOOLKIT>' )

 

The presented code snippet is meant to be a stepping stone. It should be easily adaptable to your needs. If you find improvements or you have questions, please share them here with us!

0 Comments

Leave a reply

Your email address will not be published. Required fields are marked *

*