Snapshot 테스트는 어떤 값을 (객체, 렌더된 DOM 트리, 문자열) 직렬화해서 저장하고, 다음 실행에선 live 값을 저장된 값과 비교해. 일치하면 통과. 다르면 실패 — Vitest 가 diff 를 보여줘.
그게 다야. 통상적 의미의 단언이 없어 — output 이 뭐여야 하는지 선언하는 게 아니라 지난번에 뭐였든 이번에도 그거여야 함 만 선언하는 거지. 매혹적인 부분은 코드를 얼마나 적게 쓰는지야. 위험한 부분도 같아.
두 모양 — Inline 과 File
Inline snapshot 은 테스트 파일 자체에 살아, toMatchInlineSnapshot 에 multi-line 문자열 인자로. 리뷰에서 살아남아 — diff 가 PR 안에 바로 보이니까. 짧고 안정적인 output 에 좋아.
File snapshot 은 테스트 파일과 별개의 sibling __snapshots__/ 디렉토리에 살아. 더 큰 captured output 에 확장되지만 리뷰 주의에서 잘 빠져 — 사람들이 안 읽고 PR 승인해. 아껴 써.
Snapshot 리뷰 테스트: 이 snapshot 이 바뀔 때, 누군가 실제로 diff 를 읽을까? 답이 '아니' 면 그 snapshot 은 도장이지 테스트가 아냐. 지우고 진짜 단언 짜.
업데이트 흐름 (그리고 그 함정)
Snapshot 이 정당하게 바뀐 output 때문에 실패하면 (refactor 했어, 필드 추가했어) 업데이트해: vitest -u 또는 vitest --update. 이게 현재 output 에서 snapshot 파일을 재생성하고, 너는 diff 를 커밋.
함정은 근육 기억이야. 'snapshot 실패 → U 눌러 → 커밋' 사이클 서너 번 후엔 뭐가 바뀌었는지 보는 행위가 사라져. 이제 snapshot 건드린 의도치 않은 regression 도 U 와 함께 망각으로 들어가. 테스트는 몇 달 전에 아무것도 잡지 못하게 됐는데, 실패는 항상 U 로 끝났으니까 아무도 눈치 못 챘어.
해법은 부분적으로 문화적 (snapshot-only diff 를 리뷰에서 코드 diff 와 같은 정밀로 다뤄) 이고 부분적으로 기술적 (테스트 의도와 무관한 이유로 바뀌는 걸 snapshot 하지 마).
Snapshot 이 값어치할 때
에러 메시지: 짧고, 안정적이고, 정확한 텍스트가 중요. expect(formatError(input)).toMatchInlineSnapshot('"Expected number, got string"') 는 괜찮아.
정규화된 JSON 계약: 잠가두고 싶은 작은 response 모양. 날짜 / id 를 벗기는 serializer 와 짝지어서 snapshot 이 결정적이게 만들어.
CLI output: 특정 라인을 출력하는 command-line 도구는 렌더된 output 에 대한 snapshot 테스트의 혜택을 봐.
안 될 때
React 컴포넌트의 큰 HTML 트리: Tailwind 클래스 변경마다 snapshot 깨져. 신호 대 노이즈 빠르게 떨어져.
날짜, id, hash 가 들어간 output: default 로 비결정적. serializer 추가하거나 snapshot 말고 모양에 단언.
집중된 단언을 짤 수 없는 거. Snapshot 은 뭐가 참이어야 하는지 생각하는 것의 대체가 아냐.
Snapshot 안 함이 default.expect(thing.foo).toBe('bar') 짤 수 있으면 그걸 써. Snapshot 은 output 이 진짜로 다루기 힘들고 진짜로 lock 다운이 필요한 fallback 이야. 두 절반 다 중요해 — 다루기 힘들기만 한 건 이유가 안 돼.
Snapshot 업데이트 — 그리고 왜 `u` 누르는 근육 기억을 의심해야 하는지·bash
# When a snapshot test fails because output legitimately changed:
npx vitest -u
# Or update only a specific file:
npx vitest src/lib/invoice.test.ts -u
# Watch mode: press 'u' to update failed snapshots
# press 'i' to update inline snapshots only
안티 패턴 — 컴포넌트 통째로 snapshot·typescript
// ANTI-PATTERN — don't do this
import { render } from '@testing-library/react';
import { Dashboard } from './dashboard';
it('renders the dashboard', () => {
const { container } = render(<Dashboard />);
expect(container.innerHTML).toMatchSnapshot();
// ⚠️ This will break on every Tailwind class change, every text tweak,
// every prop reshuffling. Nobody will read the diff. They'll press U.
// Write focused assertions instead — toBeInTheDocument(), toHaveRole(),
// etc. (We'll cover those in the components track.)
});
너의 코드베이스에서 snapshot 테스트 하나 찾아 (아니면 어느 오픈소스 프로젝트 — GitHub 에서 toMatchSnapshot 검색). 읽어. 물어봐: 이 snapshot 이 바뀔 때 누가 diff 를 꼼꼼히 읽을까, 아니면 U 누르고 넘어갈까? 후자면 집중된 단언으로 대체 짜 — toBe, toContain, toMatchObject. 둘 비교해: 어느 게 네가 신경 쓸 regression 을 잡을까?
Hint
대체 단언이 보통 snapshot 보다 길어. 그게 핵심이야 — 명시 버전은 뭐가 중요한지 말하고, snapshot 은 '지난번에 뭐였든' 만 말해. 길이는 명확성의 비용이야.
Progress
Progress is local-only — sign in to sync across devices.