Writing test scripts

Making HTTP requests


    Examples of performing HTTP GET requests:

    self.get(self.server_url + "/logout", description="Logout ")
    self.get(self.server_url + "/search?query=foo",
             description="Search with params in the URL")
    self.get(self.server_url + "/search", params=[['query', 'foo']],
             description="Search using params")

    Examples of performing HTTP POST requests:

    from webunit.utility import Upload
    from funkload.utils import Data
    # simple post
    self.post(self.server_url + "/login",
              params=[['user_name', 'scott'],
                      ['user_password', 'tiger']],
              description="Login as scott")
    # upload a file
    self.post(self.server_url + "/uploadFile",
              params=[['file', Upload('/tmp/foo.pdf'),
                      ['title', 'foo file']],
              description="Upload a file")
    # post with text/xml content type
    self.post(self.server_url + "/xmlAPI",
              params=Data('text/xml', '<foo>bar</foo>'),
              description="Call xml API")

    from funkload.utils import Data
    self.put(self.server_url + '/xmlAPI",
             Data('text/xml', '<foo>bar</foo>',
             description="Put query")
    self.delete(self.server_url + '/some/rest/path/object',
                description="Delete object')
  • xmlrc helper:

    ret = self.xmlrpc(server_url, 'getStatus',
                      description="Check getStatus")

You should always set a description when making a request, it improves the readability of the report.

If you run your test in debug mode you can see what is being sent. Debug mode is activated with the --debug --debug-level=3 options.

By running your test with the -V option, you will see each response in your running instance of Firefox.

Adding assertions

After each request you should add an assertion to make sure you are on the expected page.

You can check the response content using self.getBody()

self.get(server_url, description="home page")
self.assert_('Welcome' in self.getBody(), "Wrong home page")

You can check an expected HTTP return code:

ret = self.get(...)
self.assert_(ret.code in [200, 301], "expecting a 200 or 301")

Note that FunkLoad tests the HTTP return code by default, assuming that any code other than 200,301,302 is an error. This can be changed using the ok_codes parameter or the config file option:

ret = self.get(self.server_url + '/404.hmtl', ok_codes=[200, 404],
           description="Accept 404")
self.assert_(ret.code == 404)

You can check the returned URL which may be different if you have been redirected:

self.post(self.server_url + "/login",
          params=[['user_name', 'scott'],
                  ['user_password', 'tiger']],
          description="Login as scott")
self.assert_('dashboard' in self.getLastUrl(), "Login failure")

Basic Authentication

self.setBasicAuth('scott', 'tiger')
self.get(self.server_url, description="GET using basic auth")
# remove basic auth

Extra headers

self.setHeader('Accept-Language', 'de')
# this header is set for all the next requests
# Remove all additional headers

Extracting information

At some point you will need to extract information from the response. When possible, search using string methods or the re (regular expression) module. Parsing XML or HTML has such a high cost that it will prevent your tests from achieving high load.

FunkLoad comes with a simple extract_token, using string find methods:

from funkload.utils import extract_token
token = extract_token(self.getBody(), 'id="mytoken" value="', '"')

Of course, for pure functional testing you can use FunkLoad helpers:

ret = self.get(self.server_url, description="Get some page")
urls = self.listHref(url_pattern="view_document",
base_url = self.getLastBaseUrl()

Or the WebUnit minidom:

title = self.getDom().getByName('title')[0].getContents()

Or any Python XML/HTML processing library, including Beautiful Soup.

Using the configuration file

You can get information from the configuration file, using the appropriate self.conf_get*(section, key) methods:

# Getting value from the main section
value = self.conf_get('main', 'key', 'default')
count = self.conf_getInt('main', 'nb_docs', 10)
percent = self.conf_getFloat('main', 'percent', 5.5)
items = self.conf_getList('main', 'names')
# The names in the conf file are separated with a colon
# names=name1:name2:name3

Sharing credentials

If you need to share credentials among your tests you can use the FunkLoad credential server. Here is an example to request credentials:

from funkload.utils import xmlrpc_get_credential
# get the credential host and port from the config file
credential_host = self.conf_get('credential', 'host')
credential_port = self.conf_getInt('credential', 'port')
# get a login/pwd from the members group
login, password = xmlrpc_get_credential(credential_host,

Since FunkLoad 1.15 the credential server can return a sequence:

from funkload.utils import xmlrpc_get_seq
seq = xmlrpc_get_seq()

The sequence starts with 0 but can be initialized in the credential server configuration file.

Generating data

FunkLoad comes with a simple random text generator called Lipsum:

>>> from funkload.Lipsum import Lipsum
>>> print 'Word: %s\n' % (Lipsum().getWord())
Word: albus

>>> print 'UniqWord: %s\n' % (Lipsum().getUniqWord())
UniqWord: fs3ywpxg

>>> print 'Subject: %s\n' % (Lipsum().getSubject())
Subject: Fulvus orientalis albus hortensis dorsum

>>> print 'Subject uniq: %s\n' % (Lipsum().getSubject(uniq=True))
Subject uniq: F26v3y fuscus variegatus dolicho caulos cephalus

>>> print 'Sentence: %s\n' % (Lipsum().getSentence())
Sentence: Argentatus arvensis diplo familiaris tetra trich ; vulgaris montanus folius tetra so echinus, trich pteron phyton so brachy officinalis.

>>> print 'Paragraph: %s\n' % (Lipsum().getParagraph())
Paragraph: Sit pteron, tetra dermis viridis cyanos. Tetra novaehollandiae cyanos indicus major ortho archaeos montanus. Viridis cephalus, niger, it occidentalis volans delorum sativus gaster arctos phyllo dermis archaeos. Archaeos montanus erythro mauro minimus biscortborealis occidentalis morphos biscortborealis silvestris punctatus variegatus ! phyton mauro hexa.

>>> print 'Message: %s\n' % (Lipsum().getMessage())
Message: Familiaris fulvus flora xanthos tomentosus lutea lineatus ?, dolicho campus maculatus ad platy gaster punctatus. So pachys rufus tris, trich montanus so variegatus cristatus orientalis diplo minimus. Petra lateralis bradus, chilensis unus officinalis striatus ad. Xanthos dolicho arvensis ennea tinctorius phyton, sit arctos mauro.

Dermis zygos, ventrus oeos glycis dulcis chloreus verrucosus lineatus, pteron sinensis officinalis cyanos. Cephalus occidentalis verrucosus echinus ; lateralis protos tinctorius punctatus parvus volans. Pteron palustris gaster ad tomentosus platy arctos rhytis pedis indicus mono. Chilensis phyton, ; hortensis fuscus aquam.

Variegatus deca fuscus petra rubra biscortborealis familiaris sativus leucus xanthos phyton argentatus novaehollandiae brachy. Mauro rufus saurus deca oeos thrix rostra archaeos, ortho rufus phyllo cristatus campus rostra oleum xanthos chilensis. Archaeos protos tinctorius gaster arctos niger niger variegatus thrix, mauro arctos verrucosus ennea delorum. Pedis melanus mauro occidentalis pratensis chilensis arctos gaster noveboracensis, rufus ennea minimus saurus dermis fulvus octa.

>>> print 'Phone number: %s\n' % Lipsum().getPhoneNumber()
Phone number: 07 20 25 56 06

>>> print 'Phone number fr short: %s\n' % Lipsum().getPhoneNumber(
...     lang="fr", format="short")
Phone number fr short: 0787117995

>>> print 'Phone number fr medium: %s\n' % Lipsum().getPhoneNumber(
...     lang="fr", format="medium")
Phone number fr medium: 07 88 31 30 06

>>> print 'Phone number fr long: %s\n' % Lipsum().getPhoneNumber(
...     lang="fr", format="long")
Phone number fr long: +33 (0)7 41 08 36 56

>>> print 'Phone number en_US short: %s\n' % Lipsum().getPhoneNumber(
...     lang="en_US", format="short")
Phone number en_US short: 863-3655

>>> print 'Phone number en_US medium: %s\n' % Lipsum().getPhoneNumber(
...     lang="en_US", format="medium")
Phone number en_US medium: (327) 129-2863

>>> print 'Phone number en_US long: %s\n' % Lipsum().getPhoneNumber(
...     lang="en_US", format="long")
Phone number en_US long: +00 1 (283) 158-7134

>>> print 'Address default: %s' % Lipsum().getAddress()
Address default: 85 place Brevis
99612 Trich

Adding information to the report

  • At runtime a bench can add metadata to the report using the setUpBench hook and the addMetadata method:

    def setUpBench(self):
       ret = self.get(self.server_url + "/getVersion",
                      description="Get the server version")
       self.addMetadata(**{'Application version': ret.getBody()})
  • At runtime from the command line using the --label option of the bench runner.

  • After the bench using a file named funkload.metadata with a list of key:value. At the moment this file is only used by the trend reports to add chart labels and bench descriptions. This file must be put on the report directory:

    label: label used by trend report
    build: 666
    builtOn: hostname
    Text taken as description `using ReST power <http://url/>`__
    Can be multine text.


More info on the API doc: FunkLoadTestCase.