pythonrubyrestrallypyral

Rally APIs: How to copy Test Folder and member Test Cases


This question was asked by a different user earlier:

Copying Test Cases and Test Folder using Rally Python or Ruby API [closed]

but closed by moderators as being an overly broad question. However, given the inability to copy Test Folders and their member Test Cases within the Rally UI, this is a common need for Rally Users.

Thus - I'll re-pose the question, hopefully with enough detail to stand as a valid question. I'll also re-post the answers that I developed for the original question.

Question: As a Rally user and developer in the Rally Python and Ruby REST APIs: how can I leverage the Rally API toolkits to accomplish this task?


Solution

  • Ruby:

    This Ruby script will copy all Test Cases from a Source Test Folder identified by FormattedID, to a Target Test Folder, also identified by FormattedID. It will copy all Test Steps and Attachments as well. The Target Test Folder must exist, i.e. the script will not create a Test Folder for you if the Target is not found.

    The script does not associate the new Test Case to original Test Case's Work Product (i.e. Defect, User Story), or copy Discussion items, Test Case Results, or Last Build, Verdict, etc. as it is assumed the new Test Case is desired to be in a "blank" state.

    For those needing to install and configure the Ruby REST Toolkit, links are here:

    Developer Portal: Rally REST API for 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