Skip to content

Commit b277de1

Browse files
author
Luiko Czub
committed
Merge remote-tracking branch 'pade/master'
2 parents f970bd7 + eb9b8cc commit b277de1

File tree

2 files changed

+283
-49
lines changed

2 files changed

+283
-49
lines changed

test.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
'''
4+
Source file
5+
File name: test.py
6+
Creation date: 04-10-2012
7+
Author: dassier
8+
9+
'''
10+
11+
'''
12+
Fichier de test pour le module "TestLinkAPI.py"
13+
'''
14+
15+
import re
16+
from testlink import TestLink, TestLinkErrors
17+
from nose.tools import *
18+
19+
class TestClass():
20+
def setUp(self):
21+
"""Initialisation
22+
"""
23+
24+
SERVEUR_URL = "http://localhost/testlink/lib/api/xmlrpc.php"
25+
KEY = "7ec252ab966ce88fd92c25d08635672b"
26+
self.client = TestLink(server_url=SERVEUR_URL, key=KEY)
27+
28+
def test_getTestCaseIDByName(self):
29+
""" getTestCaseIDByName test
30+
"""
31+
val = self.client.getTestCaseIDByName("Fin de programme", "Séquence 2", "Test 2")
32+
# 31 is test case id
33+
assert_equal(val, '31' )
34+
35+
# Check if an error is raised in case of bad parameters
36+
assert_raises(TestLinkErrors, self.client.getTestCaseIDByName, "Initialisation", "Séquence 1", "Test 2")
37+
38+
def test_getTestProjectByName(self):
39+
project = self.client.getTestProjectByName("Test 2")
40+
assert_equals(type(project), dict)
41+
# Check if an error is raised in case of bad parameters
42+
assert_raises(TestLinkErrors, self.client.getTestProjectByName, "Unknown project")
43+
44+
def test_getTestPlanByName(self):
45+
plan_ok = self.client.getTestPlanByName("Test 2", "Full")
46+
47+
# Assume that plan id is 33
48+
assert_equal(plan_ok['id'], '33')
49+
50+
assert_raises(TestLinkErrors, self.client.getTestPlanByName, "Test 2", "Name Error")
51+
52+
def test_getBuildByName(self):
53+
pass
54+
55+
def test_reportResult(self):
56+
dico = {'testProjectName': 'Automatique',
57+
'testPlanName': 'FullAuto',
58+
'buildName': 'V0.1'}
59+
execid = self.client.reportResult("p", "test1", "S1", "An example of note", **dico)
60+
assert_equal(type(execid), str)
61+
62+
execid = self.client.reportResult("f", "test2", "S1", **dico)
63+
assert_equal(type(execid), str)
64+

TestLinkAPI.py renamed to testlink.py

+219-49
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
"""
23
34
TestLinkAPI - v0.20
@@ -6,13 +7,25 @@
67
@author: kereval.com
78
Initialy based on the James Stock testlink-api-python-client R7.
89
9-
Updated by Kereval to support testCase Reporting and File attachment to test execution
10-
10+
Update by pade to provide a user friendly library, with more robustness and error management
1111
"""
1212
import xmlrpclib
13+
import sys
14+
from datetime import date
15+
16+
class TestLinkErrors(Exception):
17+
""" Basic error handler
18+
Return message pass as argument
19+
"""
20+
def __init__(self, msg):
21+
self.__msg = msg
22+
23+
def __str__(self):
24+
return self.__msg
25+
26+
class TestLinkAPIClient(object):
27+
1328

14-
class TestlinkAPIClient:
15-
1629
def __init__(self, server_url, devKey):
1730
self.server = xmlrpclib.Server(server_url)
1831
self.devKey = devKey
@@ -139,14 +152,32 @@ def getTestCaseCustomFieldDesignValue(self, testcaseexternalid, version,
139152
'details' : str(details)}
140153
return self.server.tl.getTestCaseCustomFieldDesignValue(argsAPI)
141154

142-
def getTestCaseIDByName(self, testCaseName):
143-
""" getTestCaseIDByName :
144-
Find a test case by its name
145-
"""
155+
def getTestCaseIDByName(self, testCaseName, testSuiteName=None, testProjectName=None):
156+
"""
157+
Find a test case by its name
158+
testSuiteName and testProjectName are optionals arguments
159+
This function return a list of tests cases
160+
"""
146161
argsAPI = {'devKey' : self.devKey,
147162
'testcasename':str(testCaseName)}
148-
return self.server.tl.getTestCaseIDByName(argsAPI)
149-
163+
164+
if testSuiteName is not None:
165+
argsAPI.update({'testsuitename':str(testSuiteName)})
166+
167+
if testProjectName is not None:
168+
argsAPI.update({'testprojectname':str(testProjectName)})
169+
170+
# Server return can be a list or a dictionnary !
171+
# This function always return a list
172+
ret_srv = self.server.tl.getTestCaseIDByName(argsAPI)
173+
if type(ret_srv) == dict:
174+
retval = []
175+
for value in ret_srv.values():
176+
retval.append(value)
177+
return retval
178+
else:
179+
return ret_srv
180+
150181
def getTestCasesForTestPlan(self, *args):
151182
""" getTestCasesForTestPlan :
152183
List test cases linked to a test plan
@@ -329,12 +360,12 @@ def createTestCase(self, *args):
329360
self.stepsList = []
330361
return ret
331362

332-
def reportTCResult(self, testcaseid, testplanid, buildid, status, notes ):
363+
def reportTCResult(self, testcaseid, testplanid, buildname, status, notes ):
333364
"""
334365
Report execution result
335366
testcaseid: internal testlink id of the test case
336367
testplanid: testplan associated with the test case
337-
buildid: build version of the test case
368+
buildname: build name of the test case
338369
status: test verdict ('p': pass,'f': fail,'b': blocked)
339370
340371
Return : [{'status': True, 'operation': 'reportTCResult', 'message': 'Success!', 'overwrite': False, 'id': '37'}]
@@ -343,11 +374,13 @@ def reportTCResult(self, testcaseid, testplanid, buildid, status, notes ):
343374
argsAPI = {'devKey' : self.devKey,
344375
'testcaseid' : testcaseid,
345376
'testplanid' : testplanid,
346-
'buildid':buildid,
347377
'status': status,
348-
'notes' : notes
378+
'buildname': buildname,
379+
'notes': str(notes)
349380
}
350381
return self.server.tl.reportTCResult(argsAPI)
382+
383+
351384

352385
def uploadExecutionAttachment(self,attachmentfile,executionid,title,description):
353386
"""
@@ -486,47 +519,184 @@ def initStep(self, actions, expected_results, execution_type):
486519
Initializes the list which stores the Steps of a Test Case to create
487520
"""
488521
self.stepsList = []
489-
list = {}
490-
list['step_number'] = '1'
491-
list['actions'] = actions
492-
list['expected_results'] = expected_results
493-
list['execution_type'] = str(execution_type)
494-
self.stepsList.append(list)
522+
lst = {}
523+
lst['step_number'] = '1'
524+
lst['actions'] = actions
525+
lst['expected_results'] = expected_results
526+
lst['execution_type'] = str(execution_type)
527+
self.stepsList.append(lst)
495528
return True
496529

497530
def appendStep(self, actions, expected_results, execution_type):
498531
""" appendStep :
499532
Appends a step to the steps list
500533
"""
501-
list = {}
502-
list['step_number'] = str(len(self.stepsList)+1)
503-
list['actions'] = actions
504-
list['expected_results'] = expected_results
505-
list['execution_type'] = str(execution_type)
506-
self.stepsList.append(list)
534+
lst = {}
535+
lst['step_number'] = str(len(self.stepsList)+1)
536+
lst['actions'] = actions
537+
lst['expected_results'] = expected_results
538+
lst['execution_type'] = str(execution_type)
539+
self.stepsList.append(lst)
507540
return True
508541

509542
def getProjectIDByName(self, projectName):
510-
projects=self.server.tl.getProjects({'devKey' : self.devKey})
511-
for project in projects:
512-
if (project['name'] == projectName):
513-
result = project['id']
514-
else:
515-
result = -1
516-
return result
517-
518-
if __name__ == "__main__":
519-
myTestLinkServer = "http://YOURSERVER/testlink/lib/api/xmlrpc.php" #change
520-
myDevKey = "" # Put here your devKey
521-
myTestLink = TestlinkAPIClient(myTestLinkServer, myDevKey)
522-
print "TestLinkAPIClient - v0.2"
523-
print "@author: Olivier Renault ([email protected])"
524-
print ""
525-
if myTestLink.checkDevKey() == True:
526-
methodList = [method for method in TestlinkAPIClient.__dict__]
527-
for method in methodList:
528-
if method[0:2] != "__":
529-
print method
530-
print ""
531-
else:
532-
print "Incorrect DevKey."
543+
projects=self.server.tl.getProjects({'devKey' : self.devKey})
544+
for project in projects:
545+
if (project['name'] == projectName):
546+
result = project['id']
547+
else:
548+
result = -1
549+
return result
550+
551+
def __str__(self):
552+
message = """
553+
TestLinkAPIClient - version %s
554+
@author: Olivier Renault ([email protected])
555+
@author: kereval.com
556+
@author: Patrick Dassier
557+
558+
"""
559+
return message % self.__VERSION__
560+
561+
class TestLink(TestLinkAPIClient):
562+
"""
563+
TestLink API library
564+
"""
565+
566+
__VERSION__ = "0.1"
567+
568+
def __init__(self, server_url, key):
569+
"""
570+
Class initialisation
571+
"""
572+
super(TestLink, self).__init__(server_url, key)
573+
574+
def getTestCaseIDByName(self, testCaseName, testSuiteName, testProjectName):
575+
"""
576+
Find a test case by its name, by its suite and its project
577+
Suite name must not be duplicate, so only one test case must be found
578+
Return test case id if success
579+
or raise TestLinkErrors exception with error message in case of error
580+
"""
581+
results = super(TestLink, self).getTestCaseIDByName(testCaseName, testSuiteName, testProjectName)
582+
if results[0].has_key("message"):
583+
raise TestLinkErrors(results[0]["message"])
584+
elif len(results) > 1:
585+
raise TestLinkErrors("(getTestCaseIDByName) - Several case test found. Suite name must not be duplicate for the same project")
586+
else:
587+
if results[0]["name"] == testCaseName:
588+
return results[0]["id"]
589+
raise TestLinkErrors("(getTestCaseIDByName) - Internal server error. Return value is not expected one!")
590+
591+
592+
def reportResult(self, testResult, testCaseName, testSuiteName, testNotes="", **kwargs):
593+
"""
594+
Report results for test case
595+
Arguments are:
596+
- testResult: "p" for passed, "b" for blocked, "f" for failed
597+
- testCaseName: the test case name to report
598+
- testSuiteName: the test suite name that support the test case
599+
- testNotes: optional, if empty will be replace by a default string. To let it blank, just set testNotes to " " characters
600+
- an anonymous dictionnary with followings keys:
601+
- testProjectName: the project to fill
602+
- testPlanName: the active test plan
603+
- buildName: the active build.
604+
Raise a TestLinkErrors error with the error message in case of trouble
605+
Return the execution id needs to attach files to test execution
606+
"""
607+
608+
# Check parameters
609+
for data in ["testProjectName", "testPlanName", "buildName"]:
610+
if not kwargs.has_key(data):
611+
raise TestLinkErrors("(reportResult) - Missing key %s in anonymous dictionnary" % data)
612+
613+
# Get project id
614+
project = self.getTestProjectByName(kwargs["testProjectName"])
615+
616+
# Check if project is active
617+
if project['active'] != '1':
618+
raise TestLinkErrors("(reportResult) - Test project %s is not active" % kwargs["testProjectName"])
619+
620+
# Check test plan name
621+
plan = self.getTestPlanByName(kwargs["testProjectName"], kwargs["testPlanName"])
622+
623+
# Check is test plan is open and active
624+
if plan['is_open'] != '1' or plan['active'] != '1':
625+
raise TestLinkErrors("(reportResult) - Test plan %s is not active or not open" % kwargs["testPlanName"])
626+
# Memorise test plan id
627+
planId = plan['id']
628+
629+
# Check build name
630+
build = self.getBuildByName(kwargs["testProjectName"], kwargs["testPlanName"], kwargs["buildName"])
631+
632+
# Check if build is open and active
633+
if build['is_open'] != '1' or build['active'] != '1':
634+
raise TestLinkErrors("(reportResult) - Build %s in not active or not open" % kwargs["buildName"])
635+
636+
# Get test case id
637+
caseId = self.getTestCaseIDByName(testCaseName, testSuiteName, kwargs["testProjectName"])
638+
639+
# Check results parameters
640+
if testResult not in "pbf":
641+
raise TestLinkErrors("(reportResult) - Test result must be 'p', 'f' or 'b'")
642+
643+
if testNotes == "":
644+
# Builds testNotes if empty
645+
today = date.today()
646+
testNotes = "%s - Test performed automatically" % today.strftime("%c")
647+
elif testNotes == " ":
648+
#No notes
649+
testNotes = ""
650+
651+
print "testNotes: %s" % testNotes
652+
# Now report results
653+
results = self.reportTCResult(caseId, planId, kwargs["buildName"], testResult, testNotes)
654+
# Check errors
655+
if results[0]["message"] != "Success!":
656+
raise TestLinkErrors(results[0]["message"])
657+
658+
return results[0]['id']
659+
660+
def getTestProjectByName(self, testProjectName):
661+
"""
662+
Return project
663+
A TestLinkErrors is raised in case of error
664+
"""
665+
results = super(TestLink, self).getTestProjectByName(testProjectName)
666+
if results[0].has_key("message"):
667+
raise TestLinkErrors(results[0]["message"])
668+
669+
return results[0]
670+
671+
def getTestPlanByName(self, testProjectName, testPlanName):
672+
"""
673+
Return test plan
674+
A TestLinkErrors is raised in case of error
675+
"""
676+
results = super(TestLink, self).getTestPlanByName(testProjectName, testPlanName)
677+
if results[0].has_key("message"):
678+
raise TestLinkErrors(results[0]["message"])
679+
680+
return results[0]
681+
682+
def getBuildByName(self, testProjectName, testPlanName, buildName):
683+
"""
684+
Return build corresponding to buildName
685+
A TestLinkErrors is raised in case of error
686+
"""
687+
plan = self.getTestPlanByName(testProjectName, testPlanName)
688+
builds = self.getBuildsForTestPlan(plan['id'])
689+
690+
# Check if a builds exists
691+
if builds == '':
692+
raise TestLinkErrors("(getBuildsByName) - Builds %s does not exists for test plan %s" % (buildsName, testPlanName))
693+
694+
# Search the correct build name in the return builds list
695+
for build in builds:
696+
if build['name'] == buildName:
697+
return build
698+
699+
# No build found with builName name
700+
raise TestLinkErrors("(getBuildsByName) - Builds %s does not exists for test plan %s" % (buildsName, testPlanName))
701+
702+

0 commit comments

Comments
 (0)