7. 골든과 시각 비교
의미 있는 차이를 유지하는 작은 기준값을 검토합니다. 시각 비교의 렌더 환경, 안정화 조건, matcher 설정을 맞춥니다.
7.1 작고 의미가 남는 기준값을 검토합니다
변경의 의미를 확인할 수 있고 정규화로 중요한 차이를 숨기지 않습니다.
기준값은 안정된 입력에서 만든 검토 가능한 크기의 결과를 저장합니다. 무작위 값, 생성 시각, 무관한 런타임 정보는 통제하고 계약에 필요한 필드는 비교에 남깁니다.
긴 문자열을 길이로 바꾸거나 필드를 지우면 같은 길이의 잘못된 결과도 통과할 수 있습니다. 정규화는 계약과 무관한 차이만 제거하고 숨겨지는 중요한 속성은 별도 단언으로 보완합니다. 큰 snapshot만으로 핵심 경계값의 구체적인 단언을 대신하지 않습니다.
기준값 변경은 의도한 동작 변경을 확인한 뒤 diff를 검토합니다. 실패를 없애기 위해 일괄 갱신하지 않고 기준값 파일을 관련 변경과 함께 검토 대상으로 남깁니다.
참고: 공식 문서.
결과 문자열의 길이만 비교해 내용 변경을 놓칩니다.
import { expect, test } from "vitest";
import { toOrderRows } from "@/page/orders/_function/to-order-rows";
test("주문 행이 기준값과 같다", () => {
/**
* GIVEN
* 수량이 2인 주문
*/
const orders = [{ id: "order-1", quantity: 2, status: "open" as const }];
/**
* WHEN
* 표시용 주문 행 계산
*/
const rows = toOrderRows(orders);
/**
* THEN
* 직렬화 문자열의 길이
*/
expect(JSON.stringify(rows).length).toMatchInlineSnapshot("41");
});계약에 필요한 구조와 값을 기준값에 남깁니다.
import { expect, test } from "vitest";
import { toOrderRows } from "@/page/orders/_function/to-order-rows";
test("주문 행이 기준값과 같다", () => {
/**
* GIVEN
* 수량이 2인 주문
*/
const orders = [{ id: "order-1", quantity: 2, status: "open" as const }];
/**
* WHEN
* 표시용 주문 행 계산
*/
const rows = toOrderRows(orders);
/**
* THEN
* 행의 의미가 남는 작은 기준값
*/
expect(rows).toMatchInlineSnapshot(`
[
{
"id": "order-1",
"quantityLabel": "2.00",
},
]
`);
});7.2 시각 비교 환경과 matcher 설정을 맞춥니다
렌더 환경 차이를 통제하고 허용치가 실제 비교에 적용되게 합니다.
시각 비교는 기준값과 같은 브라우저, OS, 폰트, viewport 등 필요한 렌더 환경에서 실행합니다. 데이터와 시각을 통제하고 이미지가 안정된 뒤 필요한 영역만 비교합니다.
| 실제 비교 | 적용할 설정 |
|---|---|
| page나 locator의 toHaveScreenshot | expect.toHaveScreenshot |
| 이미지 Buffer의 toMatchSnapshot | expect.toMatchSnapshot 또는 그 호출의 옵션 |
두 matcher의 허용치 설정은 서로 대신 적용되지 않습니다. 허용치는 관측한 렌더 오차와 중요한 차이를 검출할 수 있는 근거로 정합니다. canvas의 사용자 조작이나 레이아웃 자체가 대상이면 이미지 안정화와 DOM, 위치 단언을 함께 선택합니다. 기준 이미지는 변경 결과를 직접 보고 승인하며 플랫폼별 기준의 필요성은 실행 환경에 맞게 정합니다.
참고: 공식 문서.
Buffer 비교와 다른 matcher에 허용치를 설정합니다.
import { defineConfig } from "@playwright/test";
export default defineConfig({
expect: { toHaveScreenshot: { maxDiffPixelRatio: 0.01 } },
use: { viewport: { width: 1280, height: 720 }, timezoneId: "Asia/Seoul" },
});Buffer를 비교하는 matcher에 같은 허용치를 설정합니다.
import { defineConfig } from "@playwright/test";
export default defineConfig({
expect: { toMatchSnapshot: { maxDiffPixelRatio: 0.01 } },
use: { viewport: { width: 1280, height: 720 }, timezoneId: "Asia/Seoul" },
});데이터와 렌더 완료를 확인한 이미지를 같은 matcher로 비교합니다.
import { expect, test } from "@playwright/test";
test("차트 이미지가 기준값과 같다", async ({ page }) => {
/**
* GIVEN
* 렌더가 끝난 수량 차트
*/
await page.goto("/orders/chart");
const chart = page.getByTestId("quantity-chart");
await expect(chart).toHaveAttribute("data-state", "ready");
/**
* WHEN
* 움직임을 끈 chart 이미지 캡처
*/
const image = await chart.screenshot({ animations: "disabled" });
/**
* THEN
* 동일 환경의 이미지 기준값
*/
expect(image).toMatchSnapshot("quantity-chart.png");
});