
Self-Healing Playwright: AI를 활용한 로케이터 힐링 (Locator-healing) 래퍼 구축 (Part 1)
요약
Playwright 테스트 중 로케이터 변경으로 인한 실패를 방지하기 위해 AI 기반의 Self-Healing 래퍼를 구축하는 프로젝트를 소개합니다. 기존 셀렉터가 실패할 경우 우선순위에 따라 대안 전략을 시도하여 테스트의 안정성을 높입니다.
핵심 포인트
- 로케이터 변경으로 인한 E2E 테스트 실패 문제 해결
- data-testid부터 상대적 위치까지 단계별 복구 전략 적용
- HealingEngine을 통한 자동화된 셀렉터 복구 로직 구현
- 테스트 복구율을 확인할 수 있는 HealingLog 보고서 생성
이 프로젝트의 첫 번째 파트입니다.
AI의 새로운 시대에, 저는 가끔 제가 만들고 있는 프로젝트의 주인이라는 느낌이 들지 않는다고 고백합니다. 하지만 저는 이러한 사고방식을 바꾸기로 결심했고, 기술적인 프로젝트를 수행할 때 AI의 도움을 받기 시작했으며, 이것이 그 첫 번째 프로젝트입니다. 지난 몇 달 동안 저의 역할이 QA에서 기획을 담당하고 자동화는 AI에게 맡기는 방향으로 변화함에 따라, 저는 Claude에게 더 이상 코딩을 많이 하지 않더라도 어떻게 하면 기술적 지식을 날카롭게 유지할 수 있을지에 대한 아이디어를 요청했습니다. Claude는 몇 가지 프로젝트 아이디어를 제안해 주었고, 현재 우리는 이 단계에 와 있습니다.
목차
- 문제점은 무엇인가
- 컨셉
- 프로젝트 소개
- SelfHealingPage 클래스
- HealingEngine 클래스
- HealingLog 클래스
- 테스트에서 사용하는 방법
- 배운 점 및 이 프로젝트의 향후 계획
문제점은 무엇인가
AI가 제안한 첫 번째 프로젝트이자 제가 정말 좋아했던 것은 Playwright 테스트를 위한 래퍼 (wrapper)를 구축하는 것이었습니다. 개발 중이거나 변화를 겪고 있는 프로젝트를 작업하다 보면, 업데이트되거나 심지어 변경된 로케이터 (locators) 때문에 일부 E2E 테스트가 실패할 수 있습니다. 이 프로젝트는 그 문제를 해결하기 위한 여러 가지 방법 중 하나입니다.
컨셉
기본적으로 이 프로젝트는 래퍼 (wrapper)입니다. 원래의 셀렉터 (selector)가 실패하면, HealingEngine이 작동하여 포기하기 전에 가장 신뢰할 수 있는 방법부터 가장 위험한 방법까지 일련의 대안 전략을 시도합니다:
SelfHealingPage
└── 원래의 셀렉터를 시도함
└── 실패 시? → HealingEngine
...
성공 여부와 관계없이 각 시도는 HealingLog에 기록되며, 이는 전체 테스트 스위트 (suite)에 대한 복구율 (recovery rate)을 보여주는 보고서를 생성합니다.
전략의 순서는 임의적이지 않습니다. 가장 의미론적이고 변동성이 적은 속성(data-testid)부터 가장 취약한 속성(화면상의 상대적 위치)까지의 범위를 가집니다:
| 우선순위 | 전략 | 이유 |
|---|---|---|
| 1 | data-testid (fuzzy) | 의미론적 속성, 변동성이 적음 |
| ... |
프로젝트 소개
현재 아키텍처(architecture)의 모습은 다음과 같습니다:
self-healing-playwright/
├── src/
│ ├── core/
...
The SelfHealingPage class
이 클래스는 힐러(healer)의 로직을 관리하는 메서드입니다. 테스트에서 사용된 로케이터(locator)를 가져오려고 시도합니다:
const locator = this.page.locator(selector);
try {
await locator.waitFor({ timeout: 3000 });
...
만약 이 로케이터가 존재하지 않으면, HealingEngine을 호출합니다:
catch {
console.warn(`[SelfHealing] Selector failed: "${selector}". Trying alternative strategies...`);
const context: SelectorContext = {
...
The HealingEngine class
이 클래스는 각 전략(strategy)을 가져오고, 우선순위(priority)를 확인하며, 테스트를 수정(fix)하는 역할을 담당합니다. 먼저, 우선순위에 따라 전략들을 정렬합니다:
constructor(strategies: SelectorStrategy[]) {
// 우선순위에 따라 정렬 — 가장 신뢰할 수 있는 전략부터
this.strategies = [...strategies].sort((a, b) => a.priority - b.priority);
...
그 후, SelfHealingPage가 엔진을 호출하면, 다양한 유형의 로케이터를 사용하여 테스트 힐링(healing)을 시작합니다:
get strategiesUsed(): string[] {
return this.strategies.map(s => s.name);
}
...
왜 개수(count)를 확인할까요? 때때로 요소를 찾을 때 하나 이상의 일치하는 항목이 있을 수 있습니다. 그런 경우 엔진은 다른 접근 방식을 시도합니다.
The HealingLog class
이 클래스는 힐링이 발생했을 때 이를 식별하는 것을 도와줍니다. reports 폴더 내에 다음과 같은 모델을 따르는 JSON 파일을 생성합니다:
{
"totalAttempts": 1,
"healed": 1,
...
테스트에서 사용하는 방법
힐링이 예상대로 작동하는지 확인하기 위해, Claude에게 제가 작업했던 기존 프로젝트를 가져와서 힐링 래퍼(healing wrapper)를 사용하도록 업데이트해 달라고 요청했습니다.
이 문맥에서 테스트는 Saucedemo 플랫폼에 접속을 시도합니다. 저는 로그인 버튼을 찾지 못하도록 강제로 실패하게 만들었지만, 레이블(label)을 사용하여 로케이터를 검색하도록 힌트를 주었습니다:
test.describe('Login — self-healing', () => {
test('heals a broken login button selector and still logs in', async ({ page }) => {
const shPage = createSelfHealingPage(page, log);
...
그리고 실행했을 때의 결과는 다음과 같습니다:
내가 배운 점과 이 프로젝트의 다음 단계
이제 프로젝트의 첫 번째 단계(Selector가 깨졌을 때의 힐링)가 완료되었으므로, 이를 계속해서 개선해 나가고 싶습니다.
가능한 개선 사항들을 살펴보기 전에, 이를 "구축"하면서 느꼈던 점을 공유하고 싶습니다. 솔직히 고백하자면, 제가 직접 코드를 작성하는 것이 아니라서 약간의 답답함도 있었지만, 동시에 이 첫 번째 버전을 어떻게 개선할 수 있을지, 그리고 어떻게 확장할 수 있을지에 대해 생각할 수 있는 여유를 주었습니다.
가장 먼저 개선하고 싶은 부분은 로그(log)가 생성되는 방식입니다. 현재는 실행할 때마다 새로운 리포트가 생성됩니다. 테스트가 매일 또는 모든 머지(merge) 시점에 실행될 수 있는 CI/CD 파이프라인 환경에서는, 이러한 로그를 지속적으로 유지(persist)하고 실행 간에 힐링된 로케이터(healed locators)를 재사용하는 것이 더 좋을 것입니다.
같은 주제로, 로그가 저장되는 방식 또한 개선할 기회가 있습니다. 로그를 데이터베이스(database)에 저장하는 것이 가장 좋은 해결책이 될 수 있습니다. 그렇게 하면 특정 로케이터가 빈번하게 힐링될 필요가 있는지에 대한 메트릭(metrics)을 얻을 수 있고, 이를 어떻게 수정할지 팀과 논의하거나, 심지어 우리 UI 전체에 data-testid를 어떻게 구현할지에 대해서도 논의할 수 있을 것입니다.
테스트 자체를 개선할 여지도 있습니다. 한 가지 아이디어는 피스처(fixtures)를 사용하여 SelfHealingPage를 자동으로 제공하는 것입니다. 이렇게 하면 테스트할 때마다 매번 수동으로 래퍼(wrapper)를 인스턴스화할 필요가 없어지므로, 설정(setup)이 더 깔끔해지고 전체 테스트 스위트(suite)에서 재사용하기가 더 쉬워질 것입니다.
또 다른 아이디어는 힐링(healing)이 발생할 때마다 DOM 스냅샷(snapshot)을 캡처하는 것입니다. 이를 통해 시간이 지남에 따라 데이터셋을 구축할 수 있으며, 결과적으로 이를 AI 모델에 입력하여 테스트가 실패한 후에 사후 대응하는 대신, 테스트가 실패하기 전에 올바른 로케이터(locator)를 제안하거나 심지어 예측하도록 훈련시킬 수 있을 것입니다.
마지막으로, 배포 가능성에 대해 고민하고 있습니다. 이를 독립적인 리포지토리(repository)로 유지하기보다 설치 가능한 패키지(예: npm 패키지)로 만든다면, 다른 프로젝트나 팀에 더 쉽게 플러그인(plug-in)할 수 있을 것입니다.
이것은 프로젝트의 첫 번째 파트이며, 앞으로 더 많은 내용이 이어질 예정입니다. 전체 솔루션은 여기에서 확인할 수 있습니다. 제가 이 프로젝트를 계속 구축해 나가는 동안 다음 업데이트를 기대해 주세요!
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기