YAML 기반 테스트: E2E에 대한 새로운 접근
Source: Dev.to
엔드‑투‑엔드 테스트의 유지보수 문제
전통적인 테스트 스크립트는 깨지기 쉽고, 장황하며, DOM에 밀접하게 결합되어 있습니다. UI를 한 번 리팩터링하면 전날까지 정상적으로 동작하던 수십 개의 테스트가 모두 실패할 수 있습니다. 팀은 새로운 테스트를 작성하기보다 테스트를 고치는 데 더 많은 시간을 소비하게 됩니다.
YAML 기반 테스트는 다른 접근 방식을 취합니다. 요소와 어떻게 상호작용할지를 서술하는 절차적 스크립트를 작성하는 대신, 무엇을 테스트하고 싶은지를 서술하는 선언적 파일을 작성합니다. 실행 엔진이 어떻게 수행할지를 담당합니다.
이것은 이론적인 개념이 아니라, Shiplight 가 YAML을 기본 테스트 형식으로 사용하고 있으며, 이 접근 방식은 팀이 E2E 테스트 유지보수를 생각하는 방식을 근본적으로 바꾸어 놓습니다.
왜 테스트에 YAML을 사용하는가
Readability – YAML 테스트 파일은 사용자 행동 체크리스트처럼 읽힙니다. 팀의 누구든지 — 개발자, QA 엔지니어, 제품 매니저 — YAML 테스트를 읽고 어떤 내용을 다루는지 이해할 수 있습니다. Playwright 스크립트는 JavaScript 지식과 Playwright API에 대한 친숙함이 필요합니다. YAML은 애플리케이션이 해야 할 일을 아는 것만으로 충분합니다.
Separation of intent from implementation – 기존 스크립트는 테스트 로직과 DOM‑상호작용 코드를 뒤섞습니다. 버튼의 셀렉터가 바뀌면 사용자 의도는 변하지 않았음에도 테스트가 깨집니다. YAML 기반 테스트는 무엇을 테스트하고 싶은지(의도)와 어떻게 도구가 요소를 찾고 상호작용하는지(캐시된 로케이터)를 분리합니다.
Version‑control friendliness – YAML 차이는 깔끔하고 의미가 명확합니다. 테스트가 변경될 때, diff는 정확히 어떤 동작이 바뀌었는지를 보여줍니다. JavaScript 테스트 diff는 셀렉터 업데이트, async‑처리 변경, 프레임워크 보일러플레이트 등 잡음이 많이 섞여 있습니다.
How YAML Tests Differ from Playwright Scripts
Playwright script (JavaScript)
const { test, expect } = require('@playwright/test');
test('user can log in and see dashboard', async ({ page }) => {
await page.goto('https://app.example.com/login');
await page.fill('[data-testid="email-input"]', 'user@example.com');
await page.fill('[data-testid="password-input"]', 'securepass123');
await page.click('[data-testid="login-button"]');
await page.waitForURL('**/dashboard');
await expect(page.locator('[data-testid="welcome-message"]'))
.toContainText('Welcome back');
await expect(page.locator('[data-testid="project-list"]'))
.toBeVisible();
});
Equivalent YAML test
name: User login and dashboard
url: https://app.example.com/login
statements:
- action: FILL
target: email input
value: user@example.com
- action: FILL
target: password input
value: securepass123
- action: CLICK
target: login button
- action: VERIFY
assertion: page contains "Welcome back"
- action: VERIFY
assertion: project list is visible
YAML 버전은 더 짧지만 길이가 핵심은 아닙니다. 구조적 차이가 더 중요합니다.
- Playwright 스크립트는 7개의 셀렉터(
[data-testid="email-input"]등)를 포함하고 있으며, 프론트엔드 팀이 해당 테스트 ID를 바꾸면 깨집니다. - YAML 버전은
email input,login button과 같은 의도 기반 타깃을 사용합니다 — 요소가 무엇인지에 대한 설명이며, 찾는 방법이 아닙니다. - Playwright 스크립트는
async/await, Playwright API, 그리고 JavaScript 구조 분해 할당에 대한 지식이 필요합니다. - YAML 버전은 애플리케이션이 무엇을 하는지만 알면 됩니다.
인텐트 문장
YAML 기반 테스트의 핵심 개념은 인텐트 문장입니다. 인텐트 문장은 무엇이 일어나길 원하는지를 설명하고, 도구가 어떻게 수행해야 하는지는 지정하지 않습니다.
target: login button을 작성하면, 당신은 인텐트를 표현하는 것입니다: “사용자가 로그인 버튼이라고 식별할 요소와 상호작용하고 싶다.” 테스트 엔진은 AI 기반 요소 매칭을 사용해 이를 실제 DOM 요소로 해석합니다.
이는 button.btn-primary.auth-submit 같은 선택자나 [data-testid="login-btn"]와 근본적으로 다릅니다. 선택자는 구현 세부 사항이며, 인텐트는 사용자 입장에서의 설명입니다.
intent‑cache‑heal 패턴은 대규모 환경에서도 이를 실용적으로 만듭니다:
- First run – 엔진은 각 인텐트를 특정 로케이터로 해석하고 이를 캐시합니다.
- Subsequent runs – 캐시된 로케이터를 직접 사용하여 속도를 높입니다.
- Cache miss – 캐시된 로케이터가 실패하면(UI가 변경되었기 때문) 엔진이 AI를 사용해 인텐트를 다시 해석합니다.
캐시된 선택자의 속도와 인텐트 기반 매칭의 복원력을 동시에 얻을 수 있습니다.
캐시된 로케이터
내부적으로, 모든 인텐트 대상은 캐시된 로케이터에 의해 지원됩니다. Shiplight가 login button을 button[type="submit"]으로 처음 해석하면, 해당 매핑을 테스트와 함께 로케이터‑캐시 파일에 저장합니다.
# .shiplight/cache/login-test.locators.yml
- intent: email input
locator: 'input[name="email"]'
resolved_at: 2026-03-28T14:22:00Z
- intent: password input
locator: 'input[name="password"]'
resolved_at: 2026-03-28T14:22:01Z
- intent: login button
locator: 'button[type="submit"]'
resolved_at: 2026-03-28T14:22:01Z
이 캐시 파일들은 리포지토리 내에 존재하며 — 버전‑관리되고 검토 가능한 아티팩트입니다. 로케이터가 치유(UI 변경 후 재해석)되면 캐시 파일이 업데이트되고, 차이점(diff)에는 정확히 어떤 부분이 변경되었는지 표시됩니다. 각 로케이터가 언제 마지막으로 해석되었는지, 그리고 어떻게 진화했는지를 확인할 수 있습니다.
투명성은 테스트 인프라를 감사해야 하는 팀에게 중요합니다. 숨겨진 것이 없습니다.
VERIFY 어설션
YAML 기반 테스트는 VERIFY 단계를 사용하여 어설션을 수행합니다. 특정 DOM 속성을 확인하는 전통적인 어설션과 달리, VERIFY 단계는 페이지에 대해 사실이어야 하는 내용을 자연어로 표현합니다.
- action: VERIFY
assertion: page contains "Welcome back"
- action: VERIFY
assertion: project list shows at least 3 items
- action: VERIFY
assertion: navigation menu is visible
- action: VERIFY
assertion: error message is not displayed
테스트 엔진은 수행해야 할 적절한 DOM 검사를 자동으로 결정합니다. 어설션은 UI 프레임워크가 콘텐츠를 <div>, <span>, <p> 등 어떤 요소로 렌더링하든 상관없이 동작합니다. 구현이 아니라 결과를 기술하는 것입니다.
전체 YAML 테스트 예시
다음은 전자상거래 결제 흐름에 대한 전체 YAML 테스트이며, 사용 가능한 모든 액션과 어설션을 보여줍니다:
name: Complete checkout flow
url: https://store.example.com
tags:
- checkout
- critical-path
statements:
- action: CLICK
target: first product card
- action: VERIFY
assertion: product detail page is displayed
- action: CLICK
target: add to cart button
- action: VERIFY
assertion: cart badge shows "1"
- action: CLICK
target: cart icon
- action: VERIFY
assertion: cart contains 1 item
- action: CLICK
target: proceed to checkout
- action: FILL
target: shipping address
value: 123 Test Street, San Francisco, CA 94102
- action: FILL
target: card number
value: "4242424242424242"
- action: FILL
target: expiration date
value: "12/28"
- action: FILL
target: CVV
value: "123"
- action: CLICK
target: place order button
- action: VERIFY
assertion: order confirmation page is displayed
- action: VERIFY
assertion: page contains "Thank you for your order"
- action: VERIFY
assertion: order number is displayed
이 테스트는 결제 흐름 자체가 변경되지 않는 한, 전체 프론트엔드 디자인이 새로워져도 살아남을 가능성이 높습니다. 동일한 흐름을 Playwright 스크립트로 구현하면 선택자, 대기 및 어설션이 현재 DOM에 밀접하게 결합된 60~80줄의 JavaScript 코드가 될 것입니다.
Getting Started
현재 Playwright 스크립트를 작성하고 있다면, 모든 것을 한 번에 다시 작성할 필요는 없습니다. Shiplight는 기존 테스트 스위트와 함께 plugin system을 통해 실행됩니다.
실용적인 시작점:
- UI 변경으로 인해 자주 깨지는, 가장 많이 유지 관리되는 테스트를 식별합니다.
- 해당 테스트를 먼저 YAML 형식으로 변환하고 기존 스크립트와 병행하여 실행합니다.
- 유지 관리 부담이 감소하는지 관찰합니다.
AI 코딩 에이전트를 사용해 테스트를 생성하는 팀에게는 YAML이 자연스러운 출력 형식입니다. AI가 생성한 JavaScript 테스트 스크립트는 검토가 어렵고 유지 비용이 높습니다. AI가 만든 YAML 테스트 파일은 사양처럼 읽히며 시간이 지나도 가독성을 유지합니다.
