Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Next challenges

Recent achievements

  • Global
  • Personal

Recognition

  • Give kudos
  • Received
  • Given

Leaderboard

  • Global

Trophy case

Kudos (beta program)

Kudos logo

You've been invited into the Kudos (beta program) private group. Chat with others in the program, or give feedback to Atlassian.

View group

It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Moving or cloning Zephyr tests, test cycles & test executions from one Jira project to another

Hi,

Is there a way to move or clone Zephyr tests, test cycles & test executions  (including results) from one Jira project to another Jira project?  This is for Jira server.

I could only find the importer tool which imports only tests, and none for test cycles and test executions. While the export tools are for test cycles and test executions.

 

Thanks and regards,

Marianne

2 answers

0 votes
Ivan Lima Community Leader Jun 12, 2020

Hey @Marianne Lee _Nagarro_ 

Did you take a look at zfj-importer? In theory, you should be able to migrate data - including detailed test steps - from one instance to another through the exporting/importing mechanism.

Cheers,

IL.

I wrote a Python script to migrate Zephyr test steps and their attachments from one Jira instance to another given they contain the same project with the same keys. The script should be easily extendable to grab test step results and executions.

import csv, mimetypes, os, requests, sys
from requests.auth import HTTPBasicAuth
from pathlib import Path

USERNAME = ''
PASSWORD = ''
BACKUP_BASE_URL = 'https://source-jira.com'
LIVE_BASE_URL = 'https://target-jira.com/'
START_ISSUE_KEY = 1
END_ISSUE_KEY = 9999 + 1
SOURCE_PRJ_KEY = 'PRJ'
DEST_PRJ_KEY = 'PRJ'

# Create attachments folder if it doesn't already exist.
if not os.path.exists('./attachments'):
Path('./attachments').mkdir()

def getTestSteps(issueId, is_backup_jira=False):
if is_backup_jira:
base_url = BACKUP_BASE_URL
else:
base_url = LIVE_BASE_URL
GET_TEST_STEPS = base_url + f'rest/zapi/latest/teststep/{issueId}?offset=0&limit=100'
resp = requests.get(GET_TEST_STEPS, auth=HTTPBasicAuth(USERNAME, PASSWORD))
json = resp.json()
if 'stepBeanCollection' not in json:
return None
return json['stepBeanCollection']

def saveAttachment(fileid, filename, is_backup_jira=False):
if is_backup_jira:
base_url = BACKUP_BASE_URL
else:
base_url = LIVE_BASE_URL
# Save file
resp = requests.get(f'{base_url}rest/zapi/latest/attachment/{fileid}/file', auth=HTTPBasicAuth(USERNAME, PASSWORD))
with open(f'./attachments/{filename}', 'wb') as f:
f.write(resp.content)

def getAttachmentDetails(fileid, is_backup_jira=False):
if is_backup_jira:
base_url = BACKUP_BASE_URL
else:
base_url = LIVE_BASE_URL
# Get file name
details_resp = requests.get(f'{base_url}rest/zapi/latest/attachment/{fileid}', auth=HTTPBasicAuth(USERNAME, PASSWORD))
return details_resp.json()

def createNewTestStep(issueId, step, data, result):
new_step_data = {
"step": step,
"data": data,
"result": result
}
resp = requests.post(LIVE_BASE_URL + f'rest/zapi/latest/teststep/{issueId}', auth=HTTPBasicAuth(USERNAME, PASSWORD), json=new_step_data)

def getIssueIdFromKey(issue_key, is_backup_jira=False):
if is_backup_jira:
base_url = BACKUP_BASE_URL
else:
base_url = LIVE_BASE_URL
resp = requests.get(base_url + f'rest/api/2/issue/{issue_key}', auth=HTTPBasicAuth(USERNAME, PASSWORD))
return resp.json()['id']

def printToLog(text, log_file):
print(text)
log_file.write(f'{text}\n')
log_file.flush()

def uploadAttachmentToTestStep(testStepId, filename, pathToFile, is_backup_jira=False):
if is_backup_jira:
base_url = BACKUP_BASE_URL
else:
base_url = LIVE_BASE_URL

with open(pathToFile, 'rb') as f:
mime_type = mimetypes.guess_type(filename)
mime_type=mime_type[0]
files = { 'file': (filename, f, f'{mime_type}', {'X-Atlassian-Token': 'nocheck'}) }
upload_url = f'{base_url}rest/zapi/latest/attachment?entityId={testStepId}&entityType=TESTSTEP'
resp = requests.post(upload_url, files=files, auth=HTTPBasicAuth(USERNAME, PASSWORD))


with open('migrate-zepyhr-attachments.log', mode='a', encoding='utf-8') as log_file:
printl = lambda text: printToLog(text, log_file)
for key in list(range(START_ISSUE_KEY, END_ISSUE_KEY)):
old_issue_key = f'{SOURCE_PRJ_KEY}-{key}'
new_issue_key = f'{DEST_PRJ_KEY}-{key}'
try:
old_issue_id = getIssueIdFromKey(old_issue_key, is_backup_jira=True)
except Exception as ex:
printl(f'No issue for old key {old_issue_key}')
printl(f'Exception: {ex}')
continue

try:
new_issue_id = getIssueIdFromKey(new_issue_key, is_backup_jira=False)
except Exception as ex:
printl(f'No issue for new key {new_issue_key}')
continue

# Check if test steps already exist in the target project.
# If they do, just add attachments.
new_test_steps = getTestSteps(new_issue_id, is_backup_jira=False)
printl(f'Getting test steps from {old_issue_key}...')
old_test_steps = getTestSteps(old_issue_id, is_backup_jira=True)

if new_test_steps:
printl(f'Issue {new_issue_key} already has test steps in the target Jira. Just adding attachments.')
else:
if not old_test_steps:
printl(f'No test steps on {old_issue_key}.')
continue
#printl(f'Adding test steps to {new_issue_key}...')
for test_step in old_test_steps:
createNewTestStep(new_issue_id, test_step['step'], test_step['data'], test_step['result'])

new_test_steps = getTestSteps(new_issue_id, is_backup_jira=False)
for new_test_step in new_test_steps:
# Find matching old test step
new_step_text = new_test_step['step']
matching_steps = list(filter(lambda old_test_step: old_test_step['step'] == new_step_text, old_test_steps))
if len(matching_steps) == 0:
printl(f'No matching test step found for "{new_step_text}". Skipping.')
continue
if len(matching_steps) > 1:
printl(f'ERROR: More than one test step found for "{new_step_text}". Skipping.')
continue
matching_step = matching_steps[0]
if len(matching_step['attachmentsMap']) == 0:
printl(f'{old_issue_key} - No attachments found for test step "{new_step_text}".')
continue
for attachment_details in matching_step['attachmentsMap']:
filename = attachment_details['fileName']
fileId = attachment_details['fileId']
fileSize = attachment_details['fileSize']
# Check if file already exists
def doesAttachmentAlreadyExist(newAttachmentDetails):
new_filename = newAttachmentDetails['fileName']
new_fileSize = newAttachmentDetails['fileSize']
return new_filename==filename and new_fileSize == fileSize
does_attachment_exist = len(list(filter(doesAttachmentAlreadyExist, new_test_step['attachmentsMap']))) > 0
if does_attachment_exist:
orderId = new_test_step['orderId']
printl(f'{new_issue_key} - Attachment {filename} already exists on step {orderId}')
continue

# Save file
saveAttachment(fileId, filename, is_backup_jira=True)
# Upload file to attachment
print(f'{new_issue_key} - uploading attachment {filename}')
uploadAttachmentToTestStep(new_test_step['id'], filename, f'./attachments/{filename}', is_backup_jira=False)
# Delete local copy of file
os.remove(f'./attachments/{filename}')


printl(f'Finished adding test steps from {old_issue_key} to {new_issue_key}.')
print('Done')

Thanks for sharing, @Ryan Clayton. I'll need to set up my test environment to check this out.

Suggest an answer

Log in or Sign up to answer
TAGS
Community showcase
Posted in Jira Software

Presenting the "Best of 2020" Jira Software roundup!

Catch up with Atlassian Product Managers in our 2020 Demo Den round-up! From Advanced Roadmaps to Code in Jira to Next-Gen Workflows, check out the videos below to help up-level your work in the new ...

7,104 views 8 28
Join discussion

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you