일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- katalonstudio
- JIRA
- CI
- 테스트플로우
- CD
- QA
- sqa
- 모바일자동화
- openssl
- 지라
- test
- github
- 모바일테스트자동화
- 테스트자동화
- 테스트케이스
- 소프트웨어qa
- 모바일앱테스트
- 카탈론
- Git
- 지라연동
- 테스트
- confluence
- 카탈론스튜디오
- testautomation
- jenkins
- Katalon
- PKI
- ci/cd
- 앱테스트자동화
- appium
Archives
- Today
- Total
흔한 QA 엔지니어
Katalon Studio & TestRail 연동하기 본문
TestRail
소프트웨어 테스트를 체계적으로 계획하고, 관리하고, 추적할 수 있게 도와주는 테스트 관리 도구입니다.
QA분들이라면 한번쯤 들어봤을거라 생각합니다. 왜 이 도구가 유명한걸까요?
TestRail 핵심 기능
테스트 케이스 관리 - 테스트 케이스를 작성하고, 섹션별로 체계적으로 분류 가능
테스트 실행 (Test Run) - 실행할 테스트 케이스만 선택해서 테스트 수행
결과 기록 - 테스트 Pass/Fail 여부, 실행자, 코멘트 기록 가능
리포트 & 히스토리 - 어떤 테스트가 언제 실패했는지, 누가 실행했는지 트래킹 가능
API 연동 - Katalon, Jenkins, Postman 등 자동화 도구와 연동 가능
팀 협업 - 여러 명이 동시에 테스트 관리 가능 (역할별 권한 설정 포함)
이외에도 여러 기능이 있지만 천천히 올려보도록 하겠습니다.
1. TestRail 내 신규 프로젝트 생성
Use a single repository for all cases - 단일 버전, 단순 구조
Use a single repository with baseline support - 버전(branch) 별 관리 필요
Use multiple test suites to manage cases - 기능별, 모듈별 관리 필요
2. TestRail API 키 생성 & 활성화
3. Test Listener 생성
Test Listener는 테스트 실행 전 후에 특정 코드를 실행할 수 있게 해주는 후킹 기능입니다.
Katalon Studio 실행 결과를 Testrail에 자동으로 보내는 역할을 합니다.
Test Listener 파일 내 자동 method 생성 옵션
옵션 선택 후 리스너 파일 생성 시 코드 작성에 용이하지만 저희는 붙여넣기 할거니까요
Generate sample Before Test Case method - Test Case 실행 전 호출되는 method
Generate sample After Test Case method - Test Case 실행 후 호출되는 method
Generate sample Before Test Suite method - Test Suite 실행 전 호출되는 method
Generate sample After Test Suite method - Test Suite 실행 후 호출되는 method
4. 스크립트 작성
주석 참고해주세요
import com.kms.katalon.core.annotation.*
import com.kms.katalon.core.context.TestCaseContext
import com.kms.katalon.core.context.TestSuiteContext
import groovy.json.JsonSlurper
import groovy.json.JsonOutput
import internal.GlobalVariable
import java.net.HttpURLConnection
import java.net.URL
import java.util.Base64
class TestrailListener {
// TestRail 설정
private static final String TESTRAIL_URL = "https://사용자명.testrail.io/index.php?/api/v2/"
private static final String TESTRAIL_USERNAME = "사용자 ID"
private static final String TESTRAIL_API_KEY = "발급받은 API KEY 값"
private static final int PROJECT_ID = 2 // 프로젝트 ID 수정
private static final int SECTION_ID = 2 // SECTION ID 수정
private static final String AUTH = Base64.getEncoder().encodeToString("${TESTRAIL_USERNAME}:${TESTRAIL_API_KEY}".getBytes("UTF-8"))
private static Set<Integer> caseIdSet = [] as Set
private static Map<Integer, Integer> resultMap = [:] // [caseId: status_id]
// 테스트 케이스 이름 추출 (예: Test Cases/Login/LoginSuccess → LoginSuccess)
private String extractTitle(String fullId) {
return fullId.split("/").last()
}
// TestRail 케이스 자동 검색 + 없으면 생성
private Integer getOrCreateTestRailCaseIdByTitle(String title) {
def url = new URL("${TESTRAIL_URL}get_cases/${PROJECT_ID}§ion_id=${SECTION_ID}")
HttpURLConnection conn = (HttpURLConnection) url.openConnection()
conn.setRequestMethod("GET")
conn.setRequestProperty("Authorization", "Basic " + AUTH)
def response = conn.inputStream.getText("UTF-8")
def json = new JsonSlurper().parseText(response)
def cases = json?.cases ?: []
def existing = cases.find { it.title == title }
if (existing) {
println("TestRail 케이스 찾음: '${title}' → ID: ${existing.id}")
return existing.id
}
// 케이스 없으면 새로 생성
println("🆕 TestRail에 케이스 없음 → 생성: '${title}'")
def payload = JsonOutput.toJson([
title: title,
template_id: 1,
type_id: 3,
priority_id: 2,
custom_steps: "자동 생성된 테스트 케이스입니다. (Katalon)"
])
def createUrl = new URL("${TESTRAIL_URL}add_case/${SECTION_ID}")
HttpURLConnection createConn = (HttpURLConnection) createUrl.openConnection()
createConn.setRequestMethod("POST")
createConn.setRequestProperty("Authorization", "Basic " + AUTH)
createConn.setRequestProperty("Content-Type", "application/json")
createConn.setDoOutput(true)
createConn.outputStream.write(payload.getBytes("UTF-8"))
def createRes = createConn.inputStream.getText("UTF-8")
def created = new JsonSlurper().parseText(createRes)
println("케이스 생성 완료 - ID: ${created.id}")
return created.id
}
// 케이스별 결과 수집
@AfterTestCase
def afterTestCase(TestCaseContext testCaseContext) {
def title = extractTitle(testCaseContext.getTestCaseId())
def caseId = getOrCreateTestRailCaseIdByTitle(title)
def status = testCaseContext.getTestCaseStatus() == "PASSED" ? 1 : 5
caseIdSet << caseId
resultMap[caseId] = status
println("케이스 결과 수집 완료 → ${title} / ID: ${caseId} / 상태: ${status}")
}
// Test Run 생성 + 결과 업로드 + Run 닫기
@AfterTestSuite
def createRunAndSendResults(TestSuiteContext context) {
if (caseIdSet.isEmpty()) {
println("결과가 없습니다. Test Run 생략")
return
}
def payload = JsonOutput.toJson([
name: "Katalon Auto Run - ${context.getTestSuiteId()}",
include_all: false,
case_ids: caseIdSet.toList()
])
def url = new URL("${TESTRAIL_URL}add_run/${PROJECT_ID}")
HttpURLConnection connection = (HttpURLConnection) url.openConnection()
connection.setRequestMethod("POST")
connection.setRequestProperty("Authorization", "Basic " + AUTH)
connection.setRequestProperty("Content-Type", "application/json")
connection.setDoOutput(true)
connection.outputStream.write(payload.getBytes("UTF-8"))
def response = connection.inputStream.getText("UTF-8")
def json = new JsonSlurper().parseText(response)
GlobalVariable.runId = json.id
println("Test Run 생성 완료 - ID: ${GlobalVariable.runId}")
// 결과 전송
caseIdSet.each { cid ->
sendResultToTestRail(cid, resultMap[cid], "[자동 전송] 실행 결과")
}
// Run 닫기
def closeUrl = new URL("${TESTRAIL_URL}close_run/${GlobalVariable.runId}")
HttpURLConnection closeConn = (HttpURLConnection) closeUrl.openConnection()
closeConn.setRequestMethod("POST")
closeConn.setRequestProperty("Authorization", "Basic " + AUTH)
closeConn.setRequestProperty("Content-Type", "application/json")
closeConn.connect()
if (closeConn.responseCode == 200) {
println("Test Run 닫기 완료")
} else {
println("Run 닫기 실패: " + closeConn.errorStream?.text)
}
}
// 결과 전송
private void sendResultToTestRail(int caseId, int status, String comment) {
def runId = GlobalVariable.runId
def url = new URL("${TESTRAIL_URL}add_result_for_case/${runId}/${caseId}")
HttpURLConnection conn = (HttpURLConnection) url.openConnection()
conn.setRequestMethod("POST")
conn.setRequestProperty("Authorization", "Basic " + AUTH)
conn.setRequestProperty("Content-Type", "application/json")
conn.setDoOutput(true)
def payload = JsonOutput.toJson([
status_id: status,
comment: comment
])
conn.outputStream.write(payload.getBytes("UTF-8"))
def response = conn.inputStream.getText("UTF-8")
println("결과 전송 완료 - 케이스 $caseId: $response")
}
}
5. 전역 변수 설정
전역 변수 설정 이유
@BeforeTestSuite에서 받은 runId를 @AfterTestCase method로 넘겨주기 위해서입니다.
6. Katalon Studio에서 Test Suite 실행 후 결과 확인
주의) 소스 코드는 Test Case ID가 아닌 이름 기반으로 작성되었기 때문에
중복 체크를 위해서는 기존 테스트 케이스의 이름과 동일해야 합니다.