개요
Jira Cloud 간 데이터 마이그레이션 시 Xray 애드온 데이터는 자동적으로 이관되지 않아 별도의 절차와 도구가 필요하다.
사전 준비 사항
Jira Cloud 인스턴스 간 Jira 데이터 복사
마이그레이션 단계
크게 두 단계로 나눌 수 있다.
1. 테스트, 테스트 세트, 조건 데이터 마이그레이션
테스트, 테스트 세트, 조건 데이터 식별
데이터를 Xray Document Generator를 통해 내보내기
CSV 형식으로 필요한 필드 정리
대상 환경에서 Xray Test Case Importer를 사용하여 데이터 업로드
2. 테스트 실행 데이터 마이그레이션
GraphQL API 사용하여 소스 환경에서 테스트 실행 데이터 추출
REST API 스크립트 작성하여 대상 환경으로 데이터 업로드
테스트, 테스트 세트, 조건 데이터 마이그레이션
Xray Document Generator로 테스트 데이터 내보내기
앱 관리 - Xray - Document Generator로 이동
상단의 + Add 클릭하여 템플릿 이름 및 파일 업로드
이슈 검색 페이지에서 내보내려는 이슈 검색 후 우측의 앱 - Xray:Document Generator 선택
업로드한 템플릿명 선택 후 Export 선택 후 Download 선택하여 파일 다운로드
해당 파일 열고 필요한 경우 편집 후 CSV파일로 저장
대상 환경에서 Xray Test Case Importer를 사용하여 데이터 업로드
대상 클라우드 인스턴스의 앱 - Xray - Test Case Importer로 이동
위에서 수정한 CSV파일 선택 후 CSV Delimiter 쉼표 설정 후 NEXT 선택
3.프로젝트 선택 후 NEXT 선택
4. 필드 매핑
5. import된 이슈 확인
이슈
Test Details 표시 안됨 → 각 이슈에서 업데이트나 앱 - test details 클릭 시 표시됨.
Test Status 필드 매핑 안됨 → 테스트 실행 가져올 때 가져와서 테스트와 연결 후 표시되는지 확인 → 표시됨
테스트 실행 데이터 마이그레이션
Xray API Key 생성
Xray Cloud GraphQL API에 액세스 하기 위해 API Key 생성 후 제공된 Client ID와 Client Secret으로 액세스 토큰을 발급 받아야한다.
- 앱 관리 - Xray - API Keys에서 API Key를 생성한다.
GraphQL 및 REST API를 사용하여 Python 스크립트 작성
get_token 함수를 사용하여 토큰 받기
def get_access_token(client_id, client_secret): """ 대상 환경에서 액세스 토큰을 발급받는 함수 :param client_id: Xray 환경의 Client ID :param client_secret: Xray 환경의 Client Secret :return: 발급받은 액세스 토큰 """ url = "https://xray.cloud.getxray.app/api/v2/authenticate" payload = { "client_id": client_id, "client_secret": client_secret } response = requests.post(url, json=payload) if response.status_code == 200: return json.loads(response.text) # 토큰 반환 else: raise Exception(f"Failed to get access token: {response.status_code}, {response.text}")
테스트 실행 데이터 추출
def fetch_test_execution_data(token): """ GraphQL API를 통해 테스트 실행 데이터를 가져오는 함수 :param token: Xray 소스 환경의 액세스 토큰 :return: GraphQL API 응답 데이터 (JSON) """ url = "https://xray.cloud.getxray.app/api/v2/graphql" headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} query = { "query": """ { getTestExecutions(jql: "project = 'ADON'", limit: 10) { total start limit results { issueId tests(limit: 10) { total start limit results { status { name } issueId testType { name } } } jira(fields: ["assignee", "reporter"]) } } } """ } try: logging.info("Sending request to GraphQL API.") response = requests.post(url, json=query, headers=headers) logging.info(f"Response received: Status code {response.status_code}") response.raise_for_status() # HTTP 상태 코드가 200이 아니면 예외 발생 return response.json() except requests.exceptions.Timeout: logging.error("Request timed out. Please try again later.") raise Exception("Request timed out. Please try again later.") except requests.exceptions.RequestException as e: logging.error(f"Failed to fetch test execution data. Error: {e}") raise Exception(f"Failed to fetch test execution data: {e}")
이슈 키 가져오기
데이터 추출 시에는 testExecutionKey 와 testKey가 issueId로 반환되지만
업로드는 testExecutionKey 와 testKey에 Jira IssueKey를 사용해야 하므로 Jira REST API를 사용하여 가져오기
import base64 def get_issue_key(issue_id, jira_base_url, jira_token, jira_user_name): """ Jira REST API를 사용하여 issueId로부터 issueKey 조회 :param issue_id: Xray에서 반환된 issueId :param jira_base_url: Jira 인스턴스 URL :param jira_token: Jira API 인증 토큰 (Bearer Token) :return: 해당 issueId의 issueKey """ url = f"{jira_base_url}/rest/api/2/issue/{issue_id}" # Basic Authentication 문자열 생성 auth_string = f"{jira_user_name}:{jira_token}" authBase64 = base64.b64encode(auth_string.encode()).decode() # Base64로 인코딩 headers = { "Authorization": f"Basic {authBase64}", "Content-Type": "application/json" } response = requests.get(url, headers=headers) if response.status_code == 200: return response.json().get("key") # JSON 응답에서 issueKey 추출 else: raise Exception(f"Failed to fetch issueKey for issueId {issue_id}: " f"{response.status_code}, {response.text}")
데이터 구조 변환
소스 환경에서 추출한 데이터를 REST API 업로드 형식에 맞게 변환
def transform_data_for_upload(source_data, jira_base_url, jira_token): """ GraphQL API 응답 데이터를 REST API로 업로드할 형식으로 변환 :param source_data: GraphQL API 응답 데이터 (JSON) :return: 변환된 데이터 리스트 """ transformed_data = [] executions = source_data.get("data", {}).get("getTestExecutions", {}).get("results", []) for execution in executions: # Jira REST API를 통해 issueId -> issueKey로 변환 try: print("issueId : " +execution.get("issueId")) test_execution_key = get_issue_key(execution.get("issueId"), jira_base_url, jira_token, jira_user_name) except Exception as e: logging.warning(f"Skipping test execution with issueId {execution.get('issueId')}: {e}") continue # 빈 tests 배열 건너뛰기 tests = execution.get("tests", {}).get("results", []) if not tests: continue execution_data = { "testExecutionKey": test_execution_key, # 변환된 issueKey 사용 "info": { "summary": f"Test Execution {execution.get('issueId')}", "description": "Migrated from source environment" }, "tests": [] } for test in tests: try: test_key = get_issue_key(test.get("issueId"), jira_base_url, jira_token, jira_user_name) # issueId -> issueKey 변환 except Exception as e: print(f"Skipping test with issueId {test.get('issueId')}: {e}") continue test_data = { "testKey": test_key, # 변환된 issueKey 사용 "testInfo": { "type": test.get("testType", {}).get("name") }, "status": test.get("status", {}).get("name", "TO DO") } execution_data["tests"].append(test_data) transformed_data.append(execution_data) return transformed_data
데이터 업로드
REST API로 대상 환경에 업로드
def upload_to_target_environment(transformed_data, target_token): """ 변환된 데이터를 Xray 대상 환경으로 업로드 :param transformed_data: 업로드할 데이터 리스트 :param target_token: 대상 환경의 액세스 토큰 """ url = "https://xray.cloud.getxray.app/api/v2/import/execution" headers = {"Authorization": f"Bearer {target_token}", "Content-Type": "application/json"} for execution in transformed_data: response = requests.post(url, json=execution, headers=headers) if response.status_code == 200: print(f"Successfully uploaded execution: {execution['testExecutionKey']}") else: print(f"Failed to upload execution: {execution['testExecutionKey']}, " f"Status: {response.status_code}, Response: {response.text}")
통합 코드
Import된 test execution 데이터 확인
Test Execution
Test