TestTrack SOAP Python Interface

TestTrack Pro is the Issue Management software from Seapine Software

TestTrack is a registered trademark of Seapine Software. http://www.seapine.com/testtrack.html

This library uses the suds library to talk to the TestTrack SDK SOAP API and includes some helpful extensions for managing your client code and interactions. There are a number of issues (and crashes) when talking to the latest TestTrack SOAP API using suds, and this library addresses those.

While this module is named testtrackpro it will work with TestTrack RM (Requirement Management) and TestTrack TCM (Test Case Management).

The TestTrack SOAP API uses a client cookie for managing login sessions. This cookie must be supplied on (almost) every API call. This library provides a client wrapper object which will manage the session cookie, and even releasing the cookie (logging off) as part of a context exit.

The TestTrack SOAP API includes entity edit locking where a write lock is implicit in every edit API call. The client must release the lock with either a save or cancelSave API call. The lock will remain for 15 minuites, making other attepts to edit fail on the entity.

Python contexts allow for dealing with safely releasing locks on success or error. All objects returned form API calls which start with the string ‘edit’ will return a context object that can be used with the ‘with’ statement. At the end of the statement block the appropriate ‘save’ API call will be made. If an exception occurs in the block, the appropriate ‘cancelSave’ API call will be made. In either situation the lock will be released.

import testtrackpro
with testtrackpro.TTP('http://hostname/', 'Project', 'username', 'password') as ttp:
    with ttp.editDefect(11, bDownloadAttachments=False) as defect:
        defect.priority = "Immediate"
    ## ttp.saveDefect(defect) is called, or ttp.cancelSave(defect.recordid) on exception.
## ttp.DatabaseLogoff() is called, even if an exception occured.

Additionally there is a new special edit context API extension when using python contexts for ignoring the edit lock error, when someone else has the edit lock on an entity. This is very useful when you do not want your script or service to error out on a failed edit lock, but instead want to continue processing.

import testtrackpro
with testtrackpro.TTP('http://hostname/', 'Project', 'username', 'password') as ttp:
    with ttp.editDefect(11, bDownloadAttachments=False, ignoreEditLockError=True) as defect:
        defect.priority = "Immediate"
    ## ttp.saveDefect(defect) is called, or ttp.cancelSave(defect.recordid) on exception.

    assert not testtrackpro.have_edit_lock(defect)

    if testtrackpro.was_saved(defect):
        # The priority was changed
        pass
    elif testtrackpro.has_errored(defect):
        # It was not saved due to an error
        pass
        if testtrackpro.edit_lock_failed(defect):
            # because the edit lock failed
            pass
        else:
            # because of some other error
            # NOTE: unless there was other code to catch and ignore the
            #       error, this code is unreachable.
            pass
## ttp.DatabaseLogoff() is called, even if an exception occured.

Library Use

TestTrack Python Interface

TestTrack Pro is the Issue Management software from Seapine Software.

TestTrack is a registered trademark of Seapine Software.

This library uses the suds library to talk to the TestTrack SDK SOAP API and includes some helpful extensions for managing your client code and interactions.

Seapine documentation provides a number of python samples for interacting with the TestTrack SOAP API, but there are a number of problems with their TestTrack Python Tutorial.

The sample tutorial has syntax and functional errors, and does not work with the latest versions of TestTrack and suds. Even with the versions of python and the suds library mentioned, the code will crash due to WSDL non-compliance issues when custom fields are used.

This module addresses these issues as well as provides a more managed interface to simplify development.

Python Contexts

Due to the implicit write locks (with 15 min timeout) on all edit API calls, clients normally would have to be very careful to trap all exceptions and capture any and all locked entities and un lock them as part of the exception handling. Thankfully python provides contexts via the ‘with’ statement that are designed for exactly this problem.

All suds objects returned be API calls which start with the string ‘edit’ will return suds objects which have been extended to be python contexts which can be used with the with statement:

with ttp.editDefect(42) as defect:
    defect.priority = "Immediate"

At the end of the with block, a call to ttp.saveDefect(defect) will be made automatically, saving any pending edits, and releasing the lock. Explicit calls to saveDefect or cancelSaveDefect also work within the context block.

If an exception occurs, then a call to ttp.cancelSaveDefect(defect.recordid) will be made automatically to release the lock, without saving the defect.

Also the TTP instance object is also a context object, and will log the session out when used in a with statement.

with testtrackpro.TTP('http://hostname/', 'Project', 'username', 'password') as ttp:
    defect = ttp.getDefect(42)
## ttp.DatabaseLogOff() implicitly called on success or error

Edit Locks

As mentioned above, only one person can be editing an entity at a time on the TestTrack server. This means that you will often get an error trying to edit an entity, and there is no clean way of detecting if an entity is currently locked or not; due to race conditions.

This leads to some painful defensive programming for this very common occurance.

failed_to_edit = []
for id in defect_ids_to_edit:
    try:
        with ttp.editDefect(id) as defect:
            defect.summary = "new changed title"
            ## bunch of other code
    except TTPAPIError, e:
        ## the edit lock error is code 22, and it is a string.
        if e.fault and e.fault.detail == "22":
            failed_to_edit.append(id)
        else:
            ## unexpected error, re-raise it.
            raise e

This is much more work than it should be. and the try/except work being done here is again what contexts are supposed to simplify. So every edit API call has been extended to have a new keyword argument ignoreEditLockError that defaults to False. When set to True it will swallow the TTPAPIError code "22". The object returned will be an empty entity of the proper type. This will be a context entity, just as if the call had succeeded. This means that the edit entity status functions will work on the entity.

failed_to_edit = []
for id in defect_ids_to_edit:
    with ttp.editDefect(id, ignoreEditLockError=True) as defect:
        ## helper to immediatly stop processing
        break_context_on_edit_lock_failure(defect)
        defect.summary = "new changed title"
        ## bunch of other code
    if edit_lock_failed(defect):
        failed_to_edit.append(id)

Note

Only the edit lock error "22" is captured. All other errors must be handled explictly in your code.

Warning

A new empty entity is returned when ignoreEditLockError is set to True. This means you MUST test to see if you have the real entity to work with.
>>> d = ttp.editDefect(1, ignoreEditLockError=True)
>>> print d.recordid
None
>>> edit_lock_failed(d)
True
>>> have_edit_lock(d)
False
>>> 

Module Documentation

class testtrackpro.TTP(url, database_name=None, username=None, password=None, cookie=None, plugins=None)[source]

Client for communicating with the TestTrack SOAP Service.

Parameters:
  • url (str) – [required] URL to the TestTrack SOAP WSDL File, or CGI EXE. should be a url which looks like http://127.0.0.1/ttsoapcgi.wsdl or can be the base website url http://hostname/.
  • database_name (str) – Name of the database (Project) to login to. May be supplied later in the DatabaseLogon() or ProjectLogon() methods.
  • username (str) – Username to authenticate with. May be supplied later in the DatabaseLogon() or ProjectLogon() methods.
  • password (str) – Password to authenticate with. May be supplied later in the DatabaseLogon() or ProjectLogon() methods.
  • cookie (long) – Cookie value from another SOAP client session. Useful for cloning a client session. If you use this argument, you should not supply the database_name, username, or password arguments.
  • plugins (list) – List of optional suds plugins.
DatabaseLogoff(ignore_exceptions=False)[source]

Log out of the SOAP API session, and release the stored client cookie.

Parameters:ignore_exceptions (bool) – Set this to true to ignore connection and authentication based API errors.

It can be useful to ignore connection and authenticaiton based errors when logging out, especially when using the client as a context. If there was an error communicating with the client, we want to ignore further errors due to the implicit logoff at the end of the context to preserve the ogitional initial connection error.

DatabaseLogon(database_name=None, username=None, password=None)[source]

Logon to the SOAP API and retrieve a new client cookie.

Parameters:
  • database_name (str) – Project database name to login to.
  • username (str) – Username to authenticate with. If supplied, this will update the client stored username. If not supplied, it will use the client stored value if one exists, and error otherwise.
  • password (str) – Password to authenticate with. If supplied, this will update the client stored username. If not supplied, it will use the client stored value if one exists, and error otherwise.

If the client is currently logged on, it will first logoff. If the username and or password are supplied they will update the client stored versions of these values. If they are not supplied, then the versions supplied on construction will be used.

Warning

The DatabaseLogon() API method has been depricated by Seapine, and should no longer be used. The ProjectLogon() API method should be used instead.

ProjectLogon(CProject=None, username=None, password=None)[source]

Logon to the SOAP API and retrieve a new client cookie.

Parameters:
  • CProject (CProject) – CProject entity describing a TestTrack Project Database. It is recommended to use the getProjectList() method to retrieve a valid CProject entity.
  • username (str) – Username to authenticate with. If supplied, this will update the client stored username. If not supplied, it will use the client stored value if one exists, and error otherwise.
  • password (str) – Password to authenticate with. If supplied, this will update the client stored username. If not supplied, it will use the client stored value if one exists, and error otherwise.

The appropriate CProject entity can be retrieved using the getProjectList() method, of constructing one using the create() method.

If the client is currently logged on, it will first logoff. If the username and or password are supplied they will update the client stored versions of these values. If they are not supplied, then the versions supplied on construction will be used.

This is now the prefered way to logon to TestTrack now that the DatabaseLogon() method has been depricated.

cancelSave(entity)[source]

Cancel the save of the edit locked context entity.

Parameters:entity (CType) – edit entity returned by a editXXXX method call.

This is handy when you have a an entity but can not easilly get to it’s type programatically. This is useful when you recieve polymorphic array data back from the TestTrack SOAP API.

In ortherwords you can do this:

with ttp.editDefect(defectnum) as defect:
    defect.something = "value"
    if badthing:
        ttp.cancelSave(defect)

Calling ttp.cancelSaveDefect(defect.recordid) is also safe and will prevent the call to saveDefect at the end of the context block.

Warning

If you are using edit contexts then calls to cancelSaveDefect with a recordid for the entity in the context which comes from a different structure will cause an error.

Example of errorful code:

def bad(recordid):
    with ttp.editDefectBeRecordId(recordid) as defect:
        ttp.cancelSaveDefect(recordid) ## bad
    ## at this point saveDefect(defect) will be called

Correct way:

def good(recordid):
    with ttp.editDefectByRecordId(recordid) as defect:
        ttp.cancelSave(defect)
    ## or
    with ttp.editDefectByRecordId(recordid) as defect:
        ttp.cancelSaveDefect(defect.recordid)
create(name)[source]

Factory Creation for TTPAPI structures

Parameters:name (str) – SOAP Entity type name.
## Create a new defect
# Create the CDefect object.
defect = ttp.create("CDefect")
#suds doesn't automatically initialize the record id
defect.recordid = 0;
defect.summary = "This is a new defect"
defect.product = "My Product"
defect.priority = "Immediate"

# Add the defect to TestTrack.
lNewNum = ttp.addDefect(defect)        


## Create a new project
project = ttp.create("CProject")
project.database = ttp.create("CDatabase")
project.database.name = "MyProject"
project.options = ttp.create("ArrayOfCProjectDataOption")
project.options.append(ttp.create("CProjectDataOption"))
project.options.append(ttp.create("CProjectDataOption"))
project.options.append(ttp.create("CProjectDataOption"))
project.options[0].name = "TestTrack Pro"  # add TTP functionality.
project.options[1].name = "TestTrack TCM"  # add TCM functionality.
project.options[2].name = "TestTrack RM"   # add RM functionality.

# Add the project to TestTrack.
ttp.ProjectLogon(project, username, password)
getProjectList(username=None, password=None)[source]

Return a list of CProject entities which the user has access to on the server.

Parameters:
  • username (str) – Username to authenticate with. If not supplied, and one was supplied on client construction, the client stored version will be used. This will not update the client state.
  • password (str) – PAssword to authenticate with. If not supplied, and one was supplied on client construction, the client stored version will be used. This will not update the client state.

The username and password are only required if they were not supplied on client creation. If supplied they will NOT replace the client stored username, password, or client cookie.

save(entity, *args, **kwdargs)[source]

Save the edit locked context entity.

Parameters:entity (CType) – edit entity returned by a editXXXX method call.

This is handy when you have a an entity but can not easilly get to it’s type programatically. This is useful when you recieve polymorphic array data back from the TestTrack SOAP API.

In ortherwords you can do this:

with ttp.editDefect(a) as defecta, ttp.editDefect(b) as defectb:
    defecta.something = "value"
    ttp.save(defect) ## release the lock immediatly
    defectb.other = defecta.other
## defectb is saved

Calling ttp.saveDefect(defecta) is also safe and will prevent the call to saveDefect(defecta) at the end of the context block.

Warning

If you are using edit contexts then calls to saveDefect with a CDefect for the same defect from a different API call will result in saveDefect being called again at the end of the context causing an error.

Example of errorful code:

def bad():
    getdefect = ttp.getDefect(24)
    with ttp.editDefectBeRecordId(getdefect.recordid) as defect:
        getdefect.priority = "Immediate"
        ttp.saveDefect(getdefect)
    ## at this point saveDefect(defect) will be called and will
    ## cause an error as there is no longer an edit lock on
    ## the defect.

Correct way:

def good(recordid):
    with ttp.editDefectByRecordId(recordid) as defect:
        ttp.cancelSave(defect)
    ## or
    with ttp.editDefectByRecordId(recordid) as defect:
        ttp.cancelSaveDefect(defect.recordid)
exception testtrackpro.TTPAPIError(*args, **kwdargs)[source]

Base Exception for all API errors.

document

SOAP document associated with the error if there is one, else will be None.

fault

Soap fault Envelope, if there is one, else will be None

reason

Low level SOAP or HTTP reason for the error. Will always be a valid string, but may be empty if no reason can be determined. Will back off to exception message.

exception testtrackpro.TTPConnectionError(*args, **kwdargs)[source]

Errors communicating with the TestTrack SOAP Service.

exception testtrackpro.TTPLogonError(*args, **kwdargs)[source]

Errors with authentication against the TestTrack SOAP Service.

testtrackpro.break_context_on_edit_lock_failure(*edit_context_entities)

Force immediate break in the with context if the edit lock failed.

with ttp.editDefect(1, ignoreEditLockError=True) as defect:
    break_context_on_edit_lock_failure(defect) ## stop processing here
    defect.priority = "Immediate"
if edit_lock_failed(defect):
    # deal with the issue
    pass

for multiple with statements:

with ttp.editDefect(1, ignoreEditLockError=True) as a:
    with ttp.editDefect(2, ignoreEditLockError=True) as b, ttp.editDefect(3, ignoreEditLockError=True) as c:
        ## will break out to top if a failed lock
        break_context_on_edit_lock_failure(a,b,c)
testtrackpro.edit_lock_failed(edit_context_entity)

Helper function to check if an edit context entity errored on getting the edit lock. This is useful in conjunction with the special edit API argument ignoreEditLockError=True, which will supress the failed edit lock exception.

with ttp.editDefect(1, ignoreEditLockError=True) as defect:
    defect.priority = "Immediate"
if edit_lock_failed(defect):
    # deal with the issue
    pass
testtrackpro.has_errored(edit_context_entity)

Helper function to check if an edit context entity errored. This is useful when you capture and ignore an error, or as part of error handling.

try:
    with ttp.editDefect(1) as defect:
        defect.priority = "Immediate"
        raise RuntimeError('some error')
except:
    # This is bad code, please do not do this.
    pass
    
if has_errored(defect):
    # deal with the issue
    pass
testtrackpro.have_edit_lock(edit_context_entity)

Helper function to check if an edit context entity is still open for edit.

with ttp.editDefect(1) as defect:
    if defect.priority == "Immediate":
        ttp.cancelSave(defect)
    # ...
    # more code
    # ...
    if have_edit_lock(defect):
        defect.Summary = "Updated Summary"

assert not have_edit_lock(defect)
testtrackpro.is_edit_context_entity(edit_context_entity)

Is this an edit context entity (object returned by ttp.editXXX()) that can be safely used with the status functions in this module, and use in contexts.

testtrackpro.was_saved(edit_context_entity)

Helper function to check if an edit context entity was saved. This is useful in conjunction with the special edit API argument ignoreEditLockError=True, which will supress the failed edit lock exception, and when the potential for a cancelSave was issued.

with ttp.editDefect(1, ignoreEditLockError=True) as defect:
    if defect.priority == "Immediate":
        ttp.cancelSave(defect)
    defect.priority = "Immediate"
if was_saved(defect):
    # The priority was changed
    pass
elif has_errored(defect):
    # It was not saved due to an error
    if edit_lock_failed(defect):
        # because the edit lock failed
        pass
    else:
        # because of some other error
        pass

License

Copyright (c) 2013, Doug Napoleone All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Indices and tables