실수 12/14: visual regressions를 잡지 못하고 있습니다
Source: Dev.to
Problem
간단한 CSS 변경으로 모바일에서 결제 버튼이 깨졌습니다. 모든 기능 테스트는 통과했지만, 화면에 보이지 않는 div가 겹쳐서 모바일 화면에서는 버튼이 보이지 않았습니다. 버튼은 올바른 텍스트와 함께 DOM에 존재했지만, 사용자는 볼 수도 클릭할 수도 없었습니다. 이런 종류의 버그는 고객이 실제로 결제를 시도할 때만 프로덕션에서 드러나는 경우가 많습니다.
기능 테스트는 동작을 검증하는 데는 뛰어나지만, 외관을 확인하지는 못합니다. 앱이 실제로 올바르게 보이는지는 알 수 없습니다.
Visual Regression Testing
시각적 회귀 테스트는 렌더링된 UI의 스크린샷을 비교하여 다양한 디바이스에서 레이아웃 문제를 잡아냅니다. Playwright에 내장된 toHaveScreenshot 매처를 사용하면 이를 간단히 구현할 수 있습니다.
Before (functional test only)
// ❌ Functional test passes, but the button is covered on mobile.
await expect(page.getByRole('button', { name: 'Checkout' })).toBeVisible();
// Passes because the button is in the DOM, even if users can't actually click it.
After (visual regression)
// ✅ Visual regression catches layout issues across devices.
test('cart page looks correct', async ({ page }) => {
await page.goto('/cart');
await expect(page).toHaveScreenshot('cart.png', {
mask: [
page.getByTestId('timestamp'), // changes every run
page.getByTestId('order-id'), // unique per session
],
maxDiffPixelRatio: 0.01,
});
});
Testing Across Viewports (Playwright config)
// 🧪 Test across viewports in playwright.config.ts
{
name: 'desktop',
use: { viewport: { width: 1280, height: 720 } },
},
{
name: 'mobile',
use: { viewport: { width: 375, height: 667 } },
},
타임스탬프나 주문 ID와 같이 동적으로 변하는 요소를 마스킹하면 불안정한 false positive를 방지할 수 있습니다.
Takeaway
- Functional tests는 무언가가 동작하는지를 검증합니다.
- Visual tests는 사용자가 실제로 보고 상호작용할 수 있는지를 검증합니다.
뷰포트 수준에서 시각적 커버리지를 추가하면 기능 테스트만으로는 잡히지 않는 레이아웃 버그가 배포되는 것을 방지할 수 있습니다.
시각적 테스트가 기능 테스트로는 전혀 잡히지 않았던 버그를 발견한 적이 있나요? 댓글로 경험을 공유해 주세요.