- 실패 시나리오 11개 리라이트 + 중복 2개 삭제 (fill_form → READ-only 패턴) - 이전 78.7% → 88.0% 개선 (+9.3%p) - 실패 9건 중 7건은 사이드바 렌더링 인프라 이슈 - 실질 기능 성공률 97.1% (66/68) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
272 lines
8.4 KiB
JSON
272 lines
8.4 KiB
JSON
{
|
|
"$schema": "E2E Test Data Management Configuration",
|
|
"version": "1.0.0",
|
|
"description": "테스트 데이터 생성, 관리, 정리를 위한 전역 설정",
|
|
"lastUpdated": "2026-01-31",
|
|
|
|
"namingConvention": {
|
|
"prefix": "E2E_TEST_",
|
|
"format": "{prefix}{entity}_{timestamp}",
|
|
"timestampFormat": "YYYYMMDD_HHmmss",
|
|
"examples": [
|
|
"E2E_TEST_게시글_20260131_110000",
|
|
"E2E_TEST_거래처_20260131_110000",
|
|
"E2E_TEST_사원_20260131_110000"
|
|
]
|
|
},
|
|
|
|
"dataTemplates": {
|
|
"freeBoard": {
|
|
"entity": "게시글",
|
|
"fields": {
|
|
"title": "E2E_TEST_게시글_{timestamp}",
|
|
"content": "E2E 자동화 테스트용 게시글입니다.\n생성시간: {datetime}\n테스트 목적: CRUD 흐름 검증"
|
|
},
|
|
"updateFields": {
|
|
"title": "E2E_TEST_게시글_수정됨_{timestamp}"
|
|
},
|
|
"identifierField": "title",
|
|
"searchableBy": ["title"]
|
|
},
|
|
|
|
"vendor": {
|
|
"entity": "거래처",
|
|
"fields": {
|
|
"name": "E2E_TEST_거래처_{timestamp}",
|
|
"businessNumber": "123-45-{random6}",
|
|
"representative": "테스트담당자",
|
|
"phone": "02-1234-5678",
|
|
"address": "서울시 테스트구 자동화로 123"
|
|
},
|
|
"updateFields": {
|
|
"representative": "수정된담당자",
|
|
"phone": "02-9999-8888"
|
|
},
|
|
"identifierField": "name",
|
|
"searchableBy": ["name", "businessNumber"]
|
|
},
|
|
|
|
"employee": {
|
|
"entity": "사원",
|
|
"fields": {
|
|
"name": "E2E_TEST_사원_{timestamp}",
|
|
"employeeNumber": "E2E{random4}",
|
|
"department": "테스트부서",
|
|
"position": "테스터",
|
|
"email": "e2e_test_{timestamp}@test.com"
|
|
},
|
|
"updateFields": {
|
|
"department": "수정부서",
|
|
"position": "시니어테스터"
|
|
},
|
|
"identifierField": "name",
|
|
"searchableBy": ["name", "employeeNumber"]
|
|
},
|
|
|
|
"deposit": {
|
|
"entity": "입금",
|
|
"fields": {
|
|
"description": "E2E_TEST_입금_{timestamp}",
|
|
"amount": 100000,
|
|
"depositor": "테스트입금자",
|
|
"accountNumber": "110-123-456789"
|
|
},
|
|
"updateFields": {
|
|
"amount": 200000,
|
|
"description": "E2E_TEST_입금_수정됨_{timestamp}"
|
|
},
|
|
"identifierField": "description",
|
|
"searchableBy": ["description", "depositor"]
|
|
},
|
|
|
|
"card": {
|
|
"entity": "카드",
|
|
"fields": {
|
|
"cardName": "E2E_TEST_카드_{timestamp}",
|
|
"cardNumber": "1234-5678-{random4}-{random4}",
|
|
"cardCompany": "테스트카드사",
|
|
"holder": "테스트소지자"
|
|
},
|
|
"updateFields": {
|
|
"holder": "수정된소지자"
|
|
},
|
|
"identifierField": "cardName",
|
|
"searchableBy": ["cardName", "cardNumber"]
|
|
}
|
|
},
|
|
|
|
"scripts": {
|
|
"generateTimestamp": "(() => { const now = new Date(); const pad = n => n.toString().padStart(2, '0'); return `${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`; })()",
|
|
|
|
"generateRandom": "(length) => Math.random().toString().substring(2, 2 + length)",
|
|
|
|
"generateTestData": "(template, timestamp) => { const data = {}; Object.entries(template.fields).forEach(([key, value]) => { if (typeof value === 'string') { data[key] = value.replace('{timestamp}', timestamp).replace('{datetime}', new Date().toLocaleString('ko-KR')).replace('{random4}', Math.random().toString().substring(2, 6)).replace('{random6}', Math.random().toString().substring(2, 8)); } else { data[key] = value; } }); return data; }",
|
|
|
|
"findTestData": "(prefix = 'E2E_TEST_') => { const rows = document.querySelectorAll('table tbody tr, [class*=\"table\"] [class*=\"row\"]'); return Array.from(rows).filter(row => row.innerText?.includes(prefix)).map(row => ({ text: row.innerText?.substring(0, 100), element: row })); }",
|
|
|
|
"cleanupTestData": "(async (prefix = 'E2E_TEST_') => { const testRows = []; const rows = document.querySelectorAll('table tbody tr'); rows.forEach(row => { if (row.innerText?.includes(prefix)) { testRows.push(row); } }); return { found: testRows.length, message: `Found ${testRows.length} test data rows to cleanup` }; })",
|
|
|
|
"verifyDataCreated": "(identifierValue) => { const pageText = document.body.innerText; return pageText.includes(identifierValue); }",
|
|
|
|
"verifyDataDeleted": "(identifierValue) => { const pageText = document.body.innerText; return !pageText.includes(identifierValue); }"
|
|
},
|
|
|
|
"lifecycle": {
|
|
"beforeTest": {
|
|
"description": "테스트 시작 전 실행",
|
|
"actions": [
|
|
"generateTimestamp",
|
|
"prepareTestData"
|
|
]
|
|
},
|
|
"afterCreate": {
|
|
"description": "데이터 생성 후 실행",
|
|
"actions": [
|
|
"verifyDataCreated",
|
|
"storeCreatedId"
|
|
]
|
|
},
|
|
"afterUpdate": {
|
|
"description": "데이터 수정 후 실행",
|
|
"actions": [
|
|
"verifyDataUpdated"
|
|
]
|
|
},
|
|
"afterDelete": {
|
|
"description": "데이터 삭제 후 실행",
|
|
"actions": [
|
|
"verifyDataDeleted"
|
|
]
|
|
},
|
|
"afterTest": {
|
|
"description": "테스트 종료 후 실행",
|
|
"actions": [
|
|
"cleanupOrphanedData",
|
|
"generateReport"
|
|
]
|
|
},
|
|
"onError": {
|
|
"description": "에러 발생 시 실행",
|
|
"actions": [
|
|
"attemptCleanup",
|
|
"logError"
|
|
]
|
|
}
|
|
},
|
|
|
|
"cleanup": {
|
|
"strategy": "immediate",
|
|
"strategies": {
|
|
"immediate": "테스트 완료 즉시 삭제 (권장)",
|
|
"batch": "테스트 세션 종료 시 일괄 삭제",
|
|
"scheduled": "정기적으로 삭제 (위험)"
|
|
},
|
|
"orphanedDataHandling": {
|
|
"description": "이전 테스트에서 정리되지 않은 데이터 처리",
|
|
"maxAge": "24h",
|
|
"autoCleanup": true,
|
|
"pattern": "E2E_TEST_*"
|
|
},
|
|
"protectedData": {
|
|
"description": "삭제하면 안되는 데이터 패턴",
|
|
"patterns": [
|
|
"가우스*",
|
|
"실제데이터*",
|
|
"운영*"
|
|
]
|
|
}
|
|
},
|
|
|
|
"validation": {
|
|
"rules": {
|
|
"uniqueIdentifier": {
|
|
"description": "테스트 데이터는 고유 식별자를 가져야 함",
|
|
"check": "timestamp 포함 여부"
|
|
},
|
|
"noProductionData": {
|
|
"description": "운영 데이터와 구분 가능해야 함",
|
|
"check": "E2E_TEST_ 접두사 필수"
|
|
},
|
|
"cleanupable": {
|
|
"description": "쉽게 식별하고 삭제 가능해야 함",
|
|
"check": "검색 가능한 필드 존재"
|
|
}
|
|
}
|
|
},
|
|
|
|
"isolation": {
|
|
"description": "테스트 데이터 격리 전략",
|
|
"levels": {
|
|
"session": {
|
|
"description": "세션별 고유 타임스탬프 사용",
|
|
"implementation": "timestamp를 세션 시작 시 한번만 생성"
|
|
},
|
|
"scenario": {
|
|
"description": "시나리오별 고유 데이터",
|
|
"implementation": "시나리오 ID + timestamp 조합"
|
|
},
|
|
"step": {
|
|
"description": "스텝별 고유 데이터",
|
|
"implementation": "스텝 번호 + timestamp 조합"
|
|
}
|
|
},
|
|
"currentLevel": "session"
|
|
},
|
|
|
|
"dependencies": {
|
|
"description": "테스트 데이터 간 의존성 관리",
|
|
"examples": {
|
|
"approval": {
|
|
"requires": ["employee", "document"],
|
|
"description": "결재 테스트는 사원과 문서 데이터 필요"
|
|
},
|
|
"payment": {
|
|
"requires": ["vendor", "invoice"],
|
|
"description": "지급 테스트는 거래처와 청구서 필요"
|
|
}
|
|
},
|
|
"resolutionOrder": "topological",
|
|
"autoCreate": true
|
|
},
|
|
|
|
"reporting": {
|
|
"trackCreated": true,
|
|
"trackDeleted": true,
|
|
"trackOrphaned": true,
|
|
"includeInTestReport": true,
|
|
"format": {
|
|
"summary": {
|
|
"created": "생성된 테스트 데이터 수",
|
|
"deleted": "삭제된 테스트 데이터 수",
|
|
"orphaned": "정리되지 않은 데이터 수"
|
|
},
|
|
"detail": {
|
|
"entity": "엔티티 유형",
|
|
"identifier": "식별자",
|
|
"createdAt": "생성 시간",
|
|
"deletedAt": "삭제 시간",
|
|
"status": "상태 (created/deleted/orphaned)"
|
|
}
|
|
}
|
|
},
|
|
|
|
"errorRecovery": {
|
|
"onCreateFailure": {
|
|
"action": "skip",
|
|
"log": true,
|
|
"continueTest": false
|
|
},
|
|
"onDeleteFailure": {
|
|
"action": "retry",
|
|
"maxRetries": 2,
|
|
"log": true,
|
|
"continueTest": true
|
|
},
|
|
"onOrphanedData": {
|
|
"action": "warn",
|
|
"autoCleanup": false,
|
|
"log": true
|
|
}
|
|
}
|
|
}
|