4 - 스냅샷 테스트

업데이트: Link

4장 스냅샷 테스트

Written by Alex Jover Morales

Jest에서 나를 놀라게 한 기능 중 하나는 스냅샷 테스트입니다. 반드시 Jest 전용 기능이 아니라 기술과 개념에 가깝지만 내가 처음 본 것은 Jest에서 였습니다.

스냅샷 테스트는 두 개의 다른 출력을 비교하여 어설션하는 기술입니다. 수행되는 방식은 종단 간 테스트의 시각적 회귀 테스트와 매우 유사합니다.

예를 들어 버튼의 스타일이 변경되지 않았는지 테스트하려면 버튼의 기본 스크린샷을 찍은 다음 테스트 제품군이 실행될 때마다 새 스크린샷을 찍고 기본과 픽셀 단위로 비교합니다. 스크린샷이 일치하면 테스트에 통과하지만 그렇지 않은 경우 해당 변경이 의도되었는지 여부에 따라 회귀가 있을 수 있습니다.

스냅샷 테스트는 동일한 기술을 적용하지만 이미지 대신 텍스트 또는 json과 같은 직렬화 가능한 출력을 기반으로 합니다. 따라서 웹 구성 요소를 렌더링하는 것이 나중에 보게 될 가장 중요한 것 중 하나가 될 수 있는 모든 종류의 출력 기반 기능을 위한 강력한 도구입니다.

스냅샷 테스트를 설명하기 위해 매개변수를 기반으로 오류 메시지를 반환하고 조건과 일치하지 않는 경우 오류를 발생시키는 함수가 있는 간단한 예를 살펴보겠습니다.

// error.js
export default function getErrorMessage(code) {
  if (code === 1) {
    return "The camel walks on a leg";
  } else if (code === 2) {
    return "Rabbits don't eat carrots";
  } else if (code === 3) {
    return "Cats don't eat mouses";
  }
  throw new Error("No error messages for that code");
}

지금까지 배운 도구를 사용하여 테스트를 시도하면 다음과 유사하게 만듭니다.

import getErrorMessage from "./error-message";

describe("getErrorMessage", () => {
  it("returns camel message when code is 1", () => {
    expect(getErrorMessage(1)).toBe("The camel walks on a leg");
  });

  it("returns rabbit message when code is 2", () => {
    expect(getErrorMessage(2)).toBe("Rabbits don't eat carrots");
  });

  it("returns cat message when code is 3", () => {
    expect(getErrorMessage(3)).toBe("Cats don't eat mouses");
  });

  it("throws an error otherwise", () => {
    expect(() => getErrorMessage(4)).toThrow("No error messages for that code");
  });
});

문제는 아마도 정확한 오류 메시지에 대해 별로 신경 쓰지 않고 하나의 일관성을 유지하기를 원한다는 것입니다. 그리고 현재 테스트에는 몇 가지 문제가 있습니다.

  • 반복적입니다. 동일한 논리로 각 사례에 대한 테스트가 있습니다.
  • 소스 코드 쪽에서 메시지를 변경할 때 테스트를 변경해야 합니다. 힌트를 기억하십시오. 자주 업데이트해야 하는 테스트는 좋은 테스트처럼 보이지 않습니다.

스냅샷 테스트를 사용하면 테스트에 동일한 값을 제공하면서 이러한 문제를 피할 수 있습니다. 특정 오류 메시지를 표시하는 대신 코드 1, 2 또는 3을 사용하면 일부 오류 메시지가 표시되고 사용하지 않으면 오류가 발생하는지 확인하고 해당 오류를 있는 그대로 유지하려고 합니다.

Jest는 스냅샷 테스트를 위한 특정 매처를 도입했습니다: 직렬화 가능한 값에 대한 toMatchSnapshot 및 발생한 오류에 대한 toThrowErrorMatchingSnapshot.
그것들을 사용하여 이전 테스트를 다시 작성해 보겠습니다.

import getErrorMessage from "./error-message";

describe("getErrorMessage", () => {
  it("returns an error for a valid code", () => {
    expect(getErrorMessage(1)).toMatchSnapshot();
    expect(getErrorMessage(2)).toMatchSnapshot();
    expect(getErrorMessage(3)).toMatchSnapshot();
  });

  it("throws an error otherwise", () => {
    expect(() => getErrorMessage(4)).toThrowErrorMatchingSnapshot();
  });
});

보시다시피 테스트, 특히 유효한 코드에 대한 테스트가 훨씬 간단해졌습니다. 그들은 이제 테스트가 스냅샷을 비교하여 입력을 기반으로 예상되는 출력을 제공하는지 확인하고 있습니다.

물론 처음 실행할 때는 스냅샷이 없습니다. 대신 다음 이미지와 같이 생성되었음을 확인할 수 있습니다.

이제 getErrorMessage 함수에서 코드가 1일 때 반환되는 값을 변경해 보겠습니다.

if (code === 1) {
  return "The dog walks on a leg";
}

이제 스냅샷 테스트가 실패했음을 알려주는 오류가 표시되어야 합니다.

보시다시피, 스냅샷과 일치하지 않는 expect 문의 줄을 가리키는 경우에도 특정 오류가 발생합니다.

이 시점에서 두 가지 경로가 있습니다.

  • 당신은 실수를했고 테스트는 당신에게 실제 오류를 제공합니다.
  • 코드를 의도적으로 변경했으므로 스냅샷이 오래되어 업데이트해야 합니다.

이 경우 의도적으로 변경했다고 가정해 보겠습니다. 이 경우 실패한 테스트는 실패할 것으로 예상되고 업데이트해야 하기 때문에 회귀가 아닙니다.

어떻게? watch 모드에서 테스트를 실행하는 경우 이전 이미지에서 다음 텍스트를 발견했을 수 있습니다.

코드 변경 사항을 검사하거나 u를 눌러 업데이트하십시오.

마찬가지로 u 키를 누르면 업데이트됩니다.

흔한 실수인 u를 빨리 누르고 싶은 유혹에 빠지지 마십시오. 테스트가 실제로 오래된 것인지 확인하고 코드를 주의 깊게 확인하십시오.

모든 watch 모드 옵션을 보려면 w 키를 누르십시오.

실패한 모든 스냅샷을 업데이트하는 u 옵션 외에도 선택적으로 하나씩 업데이트하는 i 옵션이 있습니다.

우리는 매우 간단한 예를 보았지만 스냅샷 테스트의 멋진 점은 더 복잡한 예에도 사용하기 쉽다는 것입니다. 직렬화 가능한 모든 입력에서 작동하므로 복잡한 JSON 구조, JavaScript 객체 및 DOM 요소를 비교하는 데 사용할 수 있습니다.

댓글남기기