拉力赛的API:如何复制测试文件夹和成员测试案例(Rally APIs: How to copy T

2019-08-02 07:00发布

这个问题被要求不同的用户更早版本:

使用拉力Python或Ruby API复制测试用例和测试夹[关闭]

但由主持人为过于宽泛的问题关闭。 然而,由于无法在中复制测试文件夹及其成员测试用例拉力UI ,这是拉力用户的共同需要。

因此 - 我会重新提出这个问题,希望能有足够的细节,成为有效的问题。 我也重新发布,我对原来的问题制定了答案。

问:作为一名拉力用户和开发者在集会Python和Ruby的REST API:我怎样才能充分利用拉力API工具包来完成这个任务?

Answer 1:

蟒蛇:

下面是执行此任务的脚本-它会复制所有测试用例由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)


Answer 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


Answer 3:

同样的Python脚本的改进,增加了以下功能

  • 测试文件夹(源和目的地)在参数而不是硬编码通过
  • 创作是完成递归(即复制测试文件夹和测试用例的话)
  • 如果它们已经存在(基于名称)测试用例被替换
  • API密钥的使用进行连接,而非密码
  • 尝试5次放弃之前连接到拉力赛

用它来复制TF10TF99 (已经存在): 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)


文章来源: Rally APIs: How to copy Test Folder and member Test Cases