Test
문제
컴포넌트 테스트가 전부 초록입니다. 실제 브라우저에서 열어 보면 레이아웃이 깨져 있습니다.
추천
jsdom은 레이아웃을 계산하지 않으므로 “폭이 변하지 않는다”·“줄이 안 바뀐다” 같은 주장을 원리적으로 못 잠급니다. 픽셀 자체 대신 형제 관계, 자식 순서, 중첩처럼 픽셀을 결정하는 조건을 잠그고, 무엇을 잠갔는지 테스트 이름과 주석에 정직하게 씁니다.
이유
jsdom은 CSS 레이아웃 엔진이 없습니다. getBoundingClientRect()를 불러도 값이 실제로
계산되지 않습니다. 따라서 “이 요소의 폭이 그대로다”라는 assertion은 처음부터 아무것도
검증하지 않는 문장입니다. 테스트가 초록인 이유가 “레이아웃이 맞아서”가 아니라 “레이아웃을
애초에 안 봐서”인 경우가 여기서 나옵니다.
레이아웃 회귀는 대개 jsdom이 못 보는 종류의 변화에서 생깁니다.
- 그리드에 새 자식을 넣었는데 마지막 열을 차지하는 규칙(
grid-column)이 빠지면, DOM에는 요소가 정상적으로 붙지만 실제 렌더 높이는 달라집니다. - 상태 클래스끼리 명시도(specificity)가 충돌하면, 클래스는 붙었는데 의도한 스타일이 한 번도 적용되지 않는 경우가 있습니다.
- 자식 폭을 컨테이너에 묶는 규칙이 빠지면, 클래스와 마크업은 그대로인데 실제 렌더 폭만 달라집니다.
셋 다 jsdom에서는 “요소가 존재한다”, “클래스가 붙었다”까지는 확인되지만, 그 클래스가 실제로 몇 px을 만드는지는 확인되지 않습니다. 그래서 테스트는 초록인데 화면은 깨질 수 있습니다.
더 나쁜 실패 모드도 있습니다. 테스트가 컴포넌트가 렌더하지도 않는 요소의 속성을 검사하고 있으면, 그 테스트는 구현이 들어가기 전부터 이미 통과합니다. 초록이 “구현이 맞다”는 증거가 아니라 “애초에 아무것도 검사하지 않는다”는 신호인 경우입니다. 테스트를 새로 추가했을 때 일부러 구현을 되돌려 실패하는지 확인하지 않으면 이 실패 모드를 놓칩니다.
테스트를 더 쓰는 것으로는 해결되지 않습니다. jsdom으로 잠글 수 있는 것과 없는 것의 경계를 구분해야 합니다. 잠글 수 있는 것은 형제가 몇 개인지, 자식 순서가 무엇인지, 어떤 클래스가 어떤 조건에서 붙는지 같은 DOM 구조입니다. 잠글 수 없는 것은 그 구조가 화면에서 몇 px로 렌더되는지입니다. 후자는 브라우저 실측 하네스로 옮깁니다.
예시
jsdom 테스트가 실제로 잠그는 것을 이름과 주석으로 밝힙니다.
// 이 테스트는 "세 번째 카드가 그리드 마지막 줄을 통째로 차지한다"를 검증합니다.// 실제 렌더 높이는 여기서 검증되지 않습니다 — 브라우저 하네스 쪽 책임입니다.it('세 번째 카드에 grid-column: 1 / -1이 붙는다', () => { render(<Grid items={threeItems} />); expect(screen.getByTestId('card-3')).toHaveClass('col-span-full');});레이아웃 자체를 검증하려면 저장소 밖 임시 디렉터리에 실제 스타일시트를 링크한 브라우저
하네스를 만들고, 수정 전/후를 getBoundingClientRect()로 비교합니다.
하지 말 것
jsdom 테스트로 픽셀·레이아웃을 잠갔다고 믿기. 렌더하지도 않는 요소의 속성을 검사하는 테스트는 구현 전에도 통과할 수 있습니다.
태스크별 리뷰와 태스크별 테스트만으로 끝내기. 여러 변경이 개별적으로는 그럴듯해도, 합쳐졌을 때만 보이는 문제는 전체 diff를 실제로 렌더해 보는 단계가 따로 없으면 못 잡습니다.
무엇을 잠갔는지 안 쓰기. “레이아웃 테스트”라는 이름만 붙이면 다음 사람은 jsdom의 한계를 모른 채 이 테스트가 픽셀까지 보장한다고 믿습니다.