1
+ # -*- coding: utf-8 -*-
1
2
"""
2
3
3
4
TestLinkAPI - v0.20
6
7
@author: kereval.com
7
8
Initialy based on the James Stock testlink-api-python-client R7.
8
9
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
11
11
"""
12
12
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
+
13
28
14
- class TestlinkAPIClient :
15
-
16
29
def __init__ (self , server_url , devKey ):
17
30
self .server = xmlrpclib .Server (server_url )
18
31
self .devKey = devKey
@@ -139,14 +152,32 @@ def getTestCaseCustomFieldDesignValue(self, testcaseexternalid, version,
139
152
'details' : str (details )}
140
153
return self .server .tl .getTestCaseCustomFieldDesignValue (argsAPI )
141
154
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
+ """
146
161
argsAPI = {'devKey' : self .devKey ,
147
162
'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
+
150
181
def getTestCasesForTestPlan (self , * args ):
151
182
""" getTestCasesForTestPlan :
152
183
List test cases linked to a test plan
@@ -329,12 +360,12 @@ def createTestCase(self, *args):
329
360
self .stepsList = []
330
361
return ret
331
362
332
- def reportTCResult (self , testcaseid , testplanid , buildid , status , notes ):
363
+ def reportTCResult (self , testcaseid , testplanid , buildname , status , notes ):
333
364
"""
334
365
Report execution result
335
366
testcaseid: internal testlink id of the test case
336
367
testplanid: testplan associated with the test case
337
- buildid : build version of the test case
368
+ buildname : build name of the test case
338
369
status: test verdict ('p': pass,'f': fail,'b': blocked)
339
370
340
371
Return : [{'status': True, 'operation': 'reportTCResult', 'message': 'Success!', 'overwrite': False, 'id': '37'}]
@@ -343,11 +374,13 @@ def reportTCResult(self, testcaseid, testplanid, buildid, status, notes ):
343
374
argsAPI = {'devKey' : self .devKey ,
344
375
'testcaseid' : testcaseid ,
345
376
'testplanid' : testplanid ,
346
- 'buildid' :buildid ,
347
377
'status' : status ,
348
- 'notes' : notes
378
+ 'buildname' : buildname ,
379
+ 'notes' : str (notes )
349
380
}
350
381
return self .server .tl .reportTCResult (argsAPI )
382
+
383
+
351
384
352
385
def uploadExecutionAttachment (self ,attachmentfile ,executionid ,title ,description ):
353
386
"""
@@ -486,47 +519,184 @@ def initStep(self, actions, expected_results, execution_type):
486
519
Initializes the list which stores the Steps of a Test Case to create
487
520
"""
488
521
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 )
495
528
return True
496
529
497
530
def appendStep (self , actions , expected_results , execution_type ):
498
531
""" appendStep :
499
532
Appends a step to the steps list
500
533
"""
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 )
507
540
return True
508
541
509
542
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