You've been invited into the Kudos (beta program) private group. Chat with others in the program, or give feedback to Atlassian.
View groupJoin the community to find out what other Atlassian users are discussing, debating and creating.
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
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')
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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 ...
Connect with like-minded Atlassian users at free events near you!
Find an eventConnect with like-minded Atlassian users at free events near you!
Unfortunately there are no Community Events near you at the moment.
Host an eventYou're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.