这个问题被要求不同的用户更早版本:
使用拉力Python或Ruby API复制测试用例和测试夹[关闭]
但由主持人为过于宽泛的问题关闭。 然而,由于无法在中复制测试文件夹及其成员测试用例拉力UI ,这是拉力用户的共同需要。
因此 - 我会重新提出这个问题,希望能有足够的细节,成为有效的问题。 我也重新发布,我对原来的问题制定了答案。
问:作为一名拉力用户和开发者在集会Python和Ruby的REST API:我怎样才能充分利用拉力API工具包来完成这个任务?
这个问题被要求不同的用户更早版本:
使用拉力Python或Ruby API复制测试用例和测试夹[关闭]
但由主持人为过于宽泛的问题关闭。 然而,由于无法在中复制测试文件夹及其成员测试用例拉力UI ,这是拉力用户的共同需要。
因此 - 我会重新提出这个问题,希望能有足够的细节,成为有效的问题。 我也重新发布,我对原来的问题制定了答案。
问:作为一名拉力用户和开发者在集会Python和Ruby的REST API:我怎样才能充分利用拉力API工具包来完成这个任务?
蟒蛇:
下面是执行此任务的脚本-它会复制所有测试用例由FormattedID识别的源测试文件夹,到目标测试文件夹,也FormattedID鉴定。 它会复制所有测试步骤和附件,以及。 目标测试文件夹必须存在,即脚本不会为你创建一个测试文件夹如果未找到目标。
该脚本不会将新的测试用例到最初的测试用例的工作产品(即缺陷,用户故事)相关联,或复制讨论项目,测试案例的结果,或一次创建,定案等,因为它假定新的测试用例需要是在一个“空白”状态。
对于那些需要安装和配置拉力Python的REST库:
拉力开发者门户网站:Pyral下载和安装
Pyral文档
#!/usr/bin/env python
#################################################################################################
#
# copy_test_folder.py -- Copy all Test Cases in Source Test Folder to Target. Includes Test Steps
# and attachments. Target Test Folder must exist (i.e. the script will not
# create a new targeet Test Folder for you)
#
USAGE = """
Usage: copy_test_folder.py
"""
#################################################################################################
# import needed python libs
import sys, os
import re
import string
import base64
from pprint import pprint
# import needed pyral libs
from pyral import Rally, rallySettings, RallyRESTAPIError
errout = sys.stderr.write
my_server = "rally1.rallydev.com"
my_user = "user@company.com"
my_password = "topsecret"
my_workspace = "My Workspace"
my_project = "My Project"
source_test_folder_formatted_id = "TF1"
target_test_folder_formatted_id = "TF4"
rally = Rally(my_server, my_user, my_password, workspace=my_workspace, project=my_project)
# rally = Rally(my_server, my_user, my_password, workspace=my_workspace, project=my_project, debug=True)
rally.enableLogging('copy_test_folder.log')
# Query for source and target test folders
source_test_folder_response = rally.get('TestFolder', fetch=True, query='FormattedID = %s' % source_test_folder_formatted_id)
target_test_folder_response = rally.get('TestFolder', fetch=True, query='FormattedID = %s' % target_test_folder_formatted_id)
# Check to make sure folders exist
if source_test_folder_response.resultCount == 0:
errout('No Source Test Folder Found matching Formatted ID: %s\n' % (source_test_folder_formatted_id))
sys.exit(4)
if target_test_folder_response.resultCount == 0:
errout('No Target Test Folder Found matching Formatted ID: %s\n. Target Test Folder must be created before copying.' % (target_test_folder_formatted_id))
sys.exit(4)
# Get references to source Test Folder and Test Cases, etc.
source_test_folder = source_test_folder_response.next()
source_test_cases = source_test_folder.TestCases
# Get reference to target Test Folder
target_test_folder = target_test_folder_response.next()
for source_test_case in source_test_cases:
# Create update fields for target Test Case
# Does NOT associate new Test Case to original Test Case's work product (i.e. Defect, User Story)
# Does NOT copy Discussion items - as the old Discussions are likely not desired on new Test Case
# Does NOT copy Last Build, Last Run, Last Update Date, Last Verdict as new Test Case will effectively
# be "blank" and not have any results associated to it
if source_test_case.Owner != None:
target_owner = source_test_case.Owner.ref
else:
target_owner = None
target_test_case_fields = {
"Package": source_test_case.Package,
"Description": source_test_case.Description,
"Method": source_test_case.Method,
"Name": source_test_case.Name,
"Objective": source_test_case.Objective,
"Owner": target_owner,
"PostConditions": source_test_case.PostConditions,
"PreConditions": source_test_case.PreConditions,
"Priority": source_test_case.Priority,
"Project": source_test_case.Project.ref,
"Risk": source_test_case.Risk,
"ValidationInput": source_test_case.ValidationInput,
"ValidationExpectedResult": source_test_case.ValidationExpectedResult,
"TestFolder": target_test_folder.ref,
}
# Create the target test case
try:
target_test_case = rally.create("TestCase", target_test_case_fields)
message = "Copied Source Test Case: " + source_test_case.FormattedID + \
" To: " + target_test_folder.FormattedID + ": " + target_test_folder.Name + \
": " + target_test_case.FormattedID
print message
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Copy Test Steps
# Add Test Case Steps
source_test_case_steps = source_test_case.Steps
for source_step in source_test_case_steps:
target_step_fields = {
"TestCase" : target_test_case.ref,
"StepIndex" : source_step.StepIndex,
"Input" : source_step.Input,
"ExpectedResult" : source_step.ExpectedResult
}
target_test_case_step = rally.put('TestCaseStep', target_step_fields)
print "===> Copied TestCaseStep: %s OID: %s" % (target_test_case_step.StepIndex, target_test_case_step.oid)
# Copy Attachments
source_attachments = rally.getAttachments(source_test_case)
for source_attachment in source_attachments:
# First copy the content
source_attachment_content = source_attachment.Content
target_attachment_content_fields = {
"Content": base64.encodestring(source_attachment_content)
}
try:
target_attachment_content = rally.put('AttachmentContent', target_attachment_content_fields)
print "===> Copied AttachmentContent: %s" % target_attachment_content.ref
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Next copy the attachment object
target_attachment_fields = {
"Name": source_attachment.Name,
"Description": source_attachment.Description,
"Content": target_attachment_content.ref,
"ContentType": source_attachment.ContentType,
"Size": source_attachment.Size,
"Artifact": target_test_case.ref,
"User": source_attachment.User.ref
}
try:
target_attachment = rally.put('Attachment', target_attachment_fields)
print "===> Copied Attachment: %s" % target_attachment.ref
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Copy Tags
source_tags = source_test_case.Tags;
target_tags = list()
for source_tag in source_tags:
target_tags.append({"_ref":source_tag.ref})
target_test_case_fields = {
"FormattedID": target_test_case.FormattedID,
"Tags": target_tags
}
try:
update_response = rally.update('TestCase', target_test_case_fields)
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
红宝石:
这个Ruby脚本将通过FormattedID识别的源测试文件夹,到目标测试文件夹,也FormattedID确定复制所有的测试用例。 它会复制所有测试步骤和附件,以及。 目标测试文件夹必须存在,即脚本不会为你创建一个测试文件夹如果未找到目标。
该脚本不会将新的测试用例到最初的测试用例的工作产品(即缺陷,用户故事)相关联,或复制讨论项目,测试案例的结果,或一次创建,定案等,因为它假定新的测试用例需要是在一个“空白”状态。
对于那些需要安装和配置Ruby的REST工具包,链接在此:
开发者门户网站:拉力REST API的Ruby
Github上
# Copyright 2002-2012 Rally Software Development Corp. All Rights Reserved.
require 'rally_api'
$my_base_url = "https://rally1.rallydev.com/slm"
$my_username = "user@company.com"
$my_password = "password"
$my_workspace = "My Workspace"
$my_project = "My Project"
$wsapi_version = "1.37"
# Test Folders
$source_test_folder_formatted_id = "TF4"
$target_test_folder_formatted_id = "TF8"
# Load (and maybe override with) my personal/private variables from a file...
my_vars= File.dirname(__FILE__) + "/my_vars.rb"
if FileTest.exist?( my_vars ) then require my_vars end
#==================== Make a connection to Rally ====================
config = {:base_url => $my_base_url}
config[:username] = $my_username
config[:password] = $my_password
config[:workspace] = $my_workspace
config[:project] = $my_project
config[:version] = $wsapi_version
@rally = RallyAPI::RallyRestJson.new(config)
begin
# Lookup source Test Folder
source_test_folder_query = RallyAPI::RallyQuery.new()
source_test_folder_query.type = :testfolder
source_test_folder_query.fetch = true
source_test_folder_query.query_string = "(FormattedID = \"" + $source_test_folder_formatted_id + "\")"
source_test_folder_result = @rally.find(source_test_folder_query)
# Lookup Target Test Folder
target_test_folder_query = RallyAPI::RallyQuery.new()
target_test_folder_query.type = :testfolder
target_test_folder_query.fetch = true
target_test_folder_query.query_string = "(FormattedID = \"" + $target_test_folder_formatted_id + "\")"
target_test_folder_result = @rally.find(target_test_folder_query)
if source_test_folder_result.total_result_count == 0
puts "Source Test Folder: " + $source_test_folder_formatted_id + "not found. Exiting."
exit
end
if target_test_folder_result.total_result_count == 0
puts "Target Test Folder: " + $target_test_folder_formatted_id + "not found. Target must exist before copying."
exit
end
source_test_folder = source_test_folder_result.first()
target_test_folder = target_test_folder_result.first()
# Populate full object for Target Test Folder
full_target_test_folder = target_test_folder.read
# Get Target Project
target_project = full_target_test_folder["Project"]
# Grab collection of Source Test Cases
source_test_cases = source_test_folder["TestCases"]
# Loop through Source Test Cases and Copy to Target
source_test_cases.each do |source_test_case|
# Get full object for Source Test Case
full_source_test_case = source_test_case.read
# Check if there's an Owner
if !full_source_test_case["Owner"].nil?
source_owner = full_source_test_case["Owner"]
else
source_owner = nil
end
# Populate field data from Source to Target
target_test_case_fields = {}
target_test_case_fields["Package"] = full_source_test_case["Package"]
target_test_case_fields["Description"] = full_source_test_case["Description"]
target_test_case_fields["Method"] = full_source_test_case["Method"]
target_test_case_fields["Name"] = full_source_test_case["Name"]
target_test_case_fields["Objective"] = full_source_test_case["Objective"]
target_test_case_fields["Owner"] = source_owner
target_test_case_fields["PostConditions"] = full_source_test_case["PostConditions"]
target_test_case_fields["PreConditions"] = full_source_test_case["PreConditions"]
target_test_case_fields["Priority"] = full_source_test_case["Priority"]
target_test_case_fields["Project"] = target_project
target_test_case_fields["Risk"] = full_source_test_case["Risk"]
target_test_case_fields["ValidationInput"] = full_source_test_case["ValidationInput"]
target_test_case_fields["ValidationExpectedResult"] = full_source_test_case["ValidationExpectedResult"]
target_test_case_fields["Tags"] = full_source_test_case["Tags"]
target_test_case_fields["TestFolder"] = target_test_folder
# Create the Target Test Case
begin
target_test_case = @rally.create(:testcase, target_test_case_fields)
puts "Test Case: #{full_source_test_case["FormattedID"]} successfully copied to #{full_target_test_folder["FormattedID"]}"
rescue => ex
puts "Test Case: #{full_source_test_case["FormattedID"]} not copied due to error"
puts ex
end
# Now Copy Test Steps
# Add Test Case Steps
source_test_case_steps = full_source_test_case["Steps"]
source_test_case_steps.each do |source_test_case_step|
full_source_step = source_test_case_step.read
target_step_fields = {}
target_step_fields["TestCase"] = target_test_case
target_step_fields["StepIndex"] = full_source_step["StepIndex"]
target_step_fields["Input"] = full_source_step["Input"]
target_step_fields["ExpectedResult"] = full_source_step["ExpectedResult"]
begin
target_test_case_step = @rally.create(:testcasestep, target_step_fields)
puts "===> Copied TestCaseStep: #{target_test_case_step["_ref"]}"
rescue => ex
puts "Test Case Step not copied due to error:"
puts ex
end
end
# Now Copy Attachments
source_attachments = full_source_test_case["Attachments"]
source_attachments.each do |source_attachment|
full_source_attachment = source_attachment.read
source_attachment_content = full_source_attachment["Content"]
full_source_attachment_content = source_attachment_content.read
# Create AttachmentContent Object for Target
target_attachment_content_fields = {}
target_attachment_content_fields["Content"] = full_source_attachment_content["Content"]
begin
target_attachment_content = @rally.create(:attachmentcontent, target_attachment_content_fields)
puts "===> Copied AttachmentContent: #{target_attachment_content["_ref"]}"
rescue => ex
puts "AttachmentContent not copied due to error:"
puts ex
end
# Now Create Attachment Container
target_attachment_fields = {}
target_attachment_fields["Name"] = full_source_attachment["Name"]
target_attachment_fields["Description"] = full_source_attachment["Description"]
target_attachment_fields["Content"] = target_attachment_content
target_attachment_fields["ContentType"] = full_source_attachment["ContentType"]
target_attachment_fields["Size"] = full_source_attachment["Size"]
target_attachment_fields["Artifact"] = target_test_case
target_attachment_fields["User"] = full_source_attachment["User"]
begin
target_attachment = @rally.create(:attachment, target_attachment_fields)
puts "===> Copied Attachment: #{target_attachment["_ref"]}"
rescue => ex
puts "Attachment not copied due to error:"
puts ex
end
end
end
end
同样的Python脚本的改进,增加了以下功能
用它来复制TF10
到TF99
(已经存在): python copy_test_folder TF10 TF99
#!/usr/bin/env python
#################################################################################################
#
# copy_test_folder.py -- Copy all Test Cases in Source Test Folder to Target. Includes Test Steps
# and attachments. Target Test Folder must exist (i.e. the script will not
# create a new targeet Test Folder for you)
#
USAGE = """
Usage: copy_test_folder.py TF_src TF_dest
"""
#################################################################################################
# import needed python libs
import sys, os
import re
import string
import base64
from pprint import pprint
# import needed pyral libs
from pyral import Rally, rallySettings, RallyRESTAPIError
errout = sys.stderr.write
my_server = "rally1.rallydev.com"
my_user = "name@domain.com"
my_password = "<PASSWORD>"
my_workspace = "<WORKSPACE>"
my_project = "<PROJECT>"
my_key = "<API KEY>"
def connect(nb_attempts=5):
'''Connect to Rally'''
global is_connected
global rally
#Connect
if not is_connected and nb_attempts>0:
print "...Attempting to connect to Rally for project %s..." % my_project
try:
# rally = Rally(my_server, my_user, my_password, workspace=my_workspace, project=my_project)
rally = Rally(my_server, apikey=my_key, workspace=my_workspace, project=my_project)
print "Connected to Rally for project %s (%s)" % (my_project, my_workspace)
is_connected=True
rally.enableLogging('copy_test_folder.log')
#Errors during connection (attempting to connect again)
# except Exception, details:
except AttributeError:
if nb_attempts>1:
connect(nb_attempts-1)
else:
errout('Error during connection to Rally (%s)\n' % details)
exit(4)
else: pass
def copyTF(src_TF, dest_TF):
'''Copy Test Cases from one folder into another (including children Test Folders)'''
# List names and FormattedID of existing Test Cases in destination
dest_TCs = dest_TF.TestCases
existing_dest_TC = {tc.Name:tc.FormattedID for tc in dest_TCs}
# Copy Test Cases to destination folder (replace if Test Case with same name exists)
src_TCs = src_TF.TestCases
for src_TC in src_TCs:
# Create update fields for target Test Case
# Does NOT associate new Test Case to original Test Case's WorkProduct (i.e. Defect, User Story)
# Does NOT copy Discussion items - as the old Discussions are likely not desired on new Test Case
# Does NOT copy Last Build, Last Run, Last Update Date, Last Verdict as new Test Case will effectively
# be "blank" and not have any results associated to it
tcName = src_TC.Name
dest_TC_fields = {
"Package": src_TC.Package,
"Description": src_TC.Description,
"Method": src_TC.Method,
"Name": tcName,
"Objective": src_TC.Objective,
"Owner": getattr(src_TC.Owner, 'ref', None),
"PostConditions": src_TC.PostConditions,
"PreConditions": src_TC.PreConditions,
"Priority": src_TC.Priority,
"Project": src_TC.Project.ref,
"Risk": src_TC.Risk,
"ValidationInput": src_TC.ValidationInput,
"ValidationExpectedResult": src_TC.ValidationExpectedResult,
"TestFolder": dest_TF.ref,
}
# Create/Update the target test case
try:
if existing_dest_TC.has_key(tcName):
operation = "Update"
dest_TC_fields['FormattedID'] = existing_dest_TC[tcName]
dest_TC = rally.update("TestCase", dest_TC_fields)
else:
#Create
operation = "Create"
dest_TC = rally.create("TestCase", dest_TC_fields)
message = operation + "d Source Test Case: " + src_TC.FormattedID + \
" To: " + dest_TF.FormattedID + ": " + dest_TF.Name + \
": " + dest_TC.FormattedID
print message
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Copy Test Steps
#Cleared-up all Test Steps in destination if Test Case is updated
if operation == "Update":
dest_TC_steps = dest_TC.Steps
for dest_step in dest_TC_steps:
rally.delete('TestCaseStep', dest_step.oid)
print "===> Cleared all Test Steps"
# Add Test Case Steps
src_TC_steps = src_TC.Steps
for src_step in src_TC_steps:
target_step_fields = {
"TestCase" : dest_TC.ref,
"StepIndex" : src_step.StepIndex,
"Input" : src_step.Input,
"ExpectedResult" : src_step.ExpectedResult
}
dest_TC_step = rally.put('TestCaseStep', target_step_fields)
print "===> Copied TestCaseStep: %s OID: %s" % (dest_TC_step.StepIndex, dest_TC_step.oid)
# Copy Attachments
source_attachments = rally.getAttachments(src_TC)
for source_attachment in source_attachments:
# First copy the content
source_attachment_content = source_attachment.Content
target_attachment_content_fields = {
"Content": base64.encodestring(source_attachment_content)
}
try:
target_attachment_content = rally.put('AttachmentContent', target_attachment_content_fields)
print "===> Copied AttachmentContent: %s" % target_attachment_content.ref
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Next copy the attachment object
target_attachment_fields = {
"Name": source_attachment.Name,
"Description": source_attachment.Description,
"Content": target_attachment_content.ref,
"ContentType": source_attachment.ContentType,
"Size": source_attachment.Size,
"Artifact": dest_TC.ref,
"User": source_attachment.User.ref
}
try:
target_attachment = rally.put('Attachment', target_attachment_fields)
print "===> Copied Attachment: %s" % target_attachment.ref
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Copy Tags
source_tags = src_TC.Tags;
target_tags = list()
for source_tag in source_tags:
target_tags.append({"_ref":source_tag.ref})
dest_TC_fields = {
"FormattedID": dest_TC.FormattedID,
"Tags": target_tags
}
try:
update_response = rally.update('TestCase', dest_TC_fields)
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Recursive call for each child Test Folder (after creating it)
src_children_TFs = src_TF.Children
dest_children_TFs = dest_TF.Children
existing_dest_TF = {tf.Name:tf for tf in dest_children_TFs}
for src_child_TF in src_children_TFs:
tfName = src_child_TF.Name
# Create Test Folder if needed
if existing_dest_TF.has_key(tfName):
dest_child_TF = existing_dest_TF[tfName]
else:
target_TF_fields = {
'Name': tfName,
'Parent': dest_TF.ref
}
dest_child_TF = rally.put('TestFolder', target_TF_fields)
print "Created Test folder %s (%s)" % (tfName, dest_child_TF.FormattedID)
# Copy Test Cases of this folder
copyTF(src_child_TF, dest_child_TF)
if '__main__' in __name__:
# Get source and destination test folders
src_TF_formatted_id = sys.argv[1]
dest_TF_formatted_id = sys.argv[2]
# Connect to Rally
global is_connected
is_connected = False
connect(5)
# Query for source and target test folders
global rally
src_TF_response = rally.get('TestFolder', fetch=True, query='FormattedID = %s' % src_TF_formatted_id)
dest_TF_response = rally.get('TestFolder', fetch=True, query='FormattedID = %s' % dest_TF_formatted_id)
# Check to make sure folders exist
if src_TF_response.resultCount == 0:
errout('No Source Test Folder Found matching Formatted ID: %s\n' % (src_TF_formatted_id))
sys.exit(4)
if dest_TF_response.resultCount == 0:
errout('No Target Test Folder Found matching Formatted ID: %s\n. Target Test Folder must be created before copying.' % (dest_TF_formatted_id))
sys.exit(4)
# Get Objects of source and target Test Folders
src_TF = src_TF_response.next()
dest_TF = dest_TF_response.next()
# Copy file
copyTF(src_TF, dest_TF)