2 - JavaScript에서 테스트

업데이트: Link

2장 JavaScript에서 테스트

Written by Alex Jover Morales

일부 JavaScript 테스트 및 해당 워크플로를 실습하기 전에 사용할 수 있는 도구를 비교해 보겠습니다.

도구

우리는 단위 테스트에 대한 몇 가지 옵션이 있습니다.

가장 인기있는 것들:

  • Jest : JavaScript 커뮤니티에서 사실상의 테스트 도구가 되었습니다. 훌륭한 조롱 기능, 라이브러리 예상, 스냅샷 테스트 등 테스트에 필요한 모든 것을 포함하는 최신 도구입니다. JSDom이라는 빠른 에뮬레이트 브라우저를 사용하고, 병렬로 실행되며, 다른 라이브러리와 잘 통합되며 제로 구성.
  • AVA : Jest와 함께 AVA는 테스트를 위한 또 다른 최신 도구입니다. Jest만큼 완전한 기능을 제공하지 않으며 더 많은 기능을 사용하려면 다른 도구가 필요합니다. 라이브러리나 소규모 앱에 적합하지만 이에 국한되지는 않습니다.
  • Karma + Mocha + Chai : 이들은 전통적인 테스트 스택을 준수하는 도구입니다. 다른 두 가지보다 더 성숙하지만 비교하면 더 많은 종속성, 더 느린 테스트, 더 적은 기능과 같은 몇 가지 단점이 있습니다. Karma는 실제 브라우저 환경을 제공할 수 있지만 단위 테스트에서는 필요하지 않으며 실행 속도가 상당히 느려집니다. 구성도 고통스러울 수 있습니다.

E2E 테스트, 프레임 워크를 테스트하는 가장 대부분의 플레이 높은 수준의 일부입니다 :

  • Cypress : 테스트를 위해 쉽고 간결한 API를 제공하는 것을 목표로 하는 최신 도구 중 하나입니다. 자체 엔진을 사용합니다.
  • Nightwatch : JavaScript 커뮤니티에서 가장 많이 사용되고 인기 있는 것 중 하나입니다. WebDriver 또는 Selenium에서 실행됩니다.

단순성, 인기도 및 강력함을 감안할 때 Jest를 선택하고 이 자습서에 사용할 것입니다.

Jest 설정

빈 프로젝트를 생성하여 시작하고(npm init) Jest를 설치합니다.

npm install -D jest

그리고 package.jsontest 스크립트를 생성합니다.

{
  "name": "jest-testing",
  "version": "0.1.0",
  "scripts": {
    "test": "jest"
  }
}

ECMA 스크립트 모듈 사용

지금까지 수행한 작업으로 jest는 이미 npm test를 실행 중일 것입니다. 기본적으로 Jest는 NodeJS가 require 구문과 함께 사용하는 모듈 형식인 CommonJS 모듈 을 이해합니다. importexport 구문과 함께 ECMA 스크립트 모듈을 사용하려면 몇 가지 추가 단계를 수행해야 합니다.

최신 babel-preset-env를 사용합시다. 다음을 실행하여 설치합니다.

npm install -D babel-preset-env

그런 다음 다음 내용 으로 .babelrc 파일을 만듭니다 .

{
  "presets": ["env"]
}

첫 번째 테스트 만들기

이 예제에서는 Calculator 테스트 개체로 생성해 보겠습니다 .

// calculator.js
export default class Calculator {
  add(a, b) {
    return a + b;
  }
  subtract(a, b) {
    return a - b;
  }
}

Jest에서 테스트 describe는 테스트 객체를 참조 하는 함수로 시작됩니다. 여기에는 각 테스트 케이스에 대한 여러 it 구문이 포함되어 있습니다 .

Calculator가 실제로 인스턴스화 가능한 클래스인지 테스트하는 것으로 시작하겠습니다.

// calculator.test.js
import Calculator from "./calculator";

describe("Calculator", () => {
  it("should be instanceable", () => {
    expect(new Calculator()).toBeInstanceOf(Calculator);
  });
});

보시다시피 Calculator는 첫 번째 테스트 대상이며 예상에서 toBeInstanceOf(…) 매처를 사용하여 인스턴스화할 수 있는 테스트 케이스를 설명하는 it 함수가 포함되어 있습니다. Jest’s expect documentation에서 매처의 전체 목록을 볼 수 있습니다.

Jest는 .test 또는 .spec 접미사 가 있는 테스트 파일과 폴더 __tests__ 내의 .js 파일을 자동으로 감지 합니다. 제가 계산기 테스트 calculator.test.js 라고 이름을 붙인 이유입니다.

그런 다음 npm test를 실행하면 테스트를 통과합니다. \o/

계산기 테스트

계산기에는 여러 기능이 있으며 각각은 테스트 항목입니다. 테스트 항목에는 여러 테스트 케이스도 있을 수 있습니다.

describe를 구조화하기 위해 여러 개 를 중첩할 수 있으므로 다음과 같이 됩니다.

// calculator.test.js
import Calculator from "./calculator";

describe("Calculator", () => {
  it("should be instanceable", () => {
    expect(new Calculator()).toBeInstanceOf(Calculator);
  });
  describe("add", () => {
    //...
  });
  describe("subtract", () => {
    //...
  });
});

작동해야 하는 경우에 대한 테스트를 추가해 보겠습니다.

// calculator.test.js
// ...
describe("add", () => {
  it("should sum up 2 numbers", () => {
    const calculator = new Calculator();
    expect(calculator.add(3, 2)).toBe(5);
  });
});
describe("subtract", () => {
  it("should subtract 2 numbers", () => {
    const calculator = new Calculator();
    expect(calculator.subtract(3, 2)).toBe(1);
  });
});

테스트 주도 개발(TDD - Test Driven Development)

테스트 주도 개발은 코드를 구현하기 전에 테스트를 작성하는 것을 기반으로 하는 프로그래밍 방법론입니다. 이를 통해 먼저 요구 사항을 생각한 다음 작동 방식과 모듈의 최종 API가 어떻게 생겼는지 그리고 마지막으로 구현 세부 사항을 생각하게 됩니다.

TDD와 함께 자연스럽게 제공되는 작고 지속적인 개발 주기로 인해 애자일 워크플로 방법론에 완벽하게 채택되고 적합합니다.

Jest에는 편리한 watch 모드 가 있습니다. 파일을 감시할 뿐만 아니라 변경된 파일만 실행하거나 파일 또는 테스트 이름으로 필터링하는 몇 가지 옵션도 제공합니다. watch 옵션을 사용하여 테스트 스크립트를 실행하기만 하면 됩니다.

npm test -- --watch

Jest는 내부적으로 Git을 사용하여 변경된 파일을 확인합니다.
Git을 사용하지 않는 경우 watchAll 옵션을 대신 사용하십시오.

그런 다음 TDD를 사용하여 add 메서드에 대한 더 많은 사용 사례를 정의할 수 있습니다.

// calculator.test.js
// ...
describe("add", () => {
  // ...
  it("should throw an Error if less than 2 args are supplied", () => {
    const calculator = new Calculator();
    expect(() => calculator.add(3)).toThrow();
  });
  it("should throw an Error if the arguments are not numbers", () => {
    const calculator = new Calculator();
    expect(() => calculator.add(3, "2")).toThrow();
  });
});

보시다시피, toThrow와 같은 오류 발생 매처를 사용할 때 예상되는 객체를 함수에서 래핑해야 합니다. 왜냐하면 예상은 내부적으로 try/catch로 래핑하기 때문입니다.

문자열을 인수로 전달하거나 Error 인스턴스를 toThrow 함수에 전달하여 특정 오류를 지정할 수도 있습니다.

// calculator.test.js
// ...
describe("add", () => {
  // ...
  it("should throw an Error if less than 2 args are supplied", () => {
    const calculator = new Calculator();
    expect(() => calculator.add(3)).toThrow("2 arguments are required");
  });
  it("should throw an Error if the arguments are not numbers", () => {
    const calculator = new Calculator();
    expect(() => calculator.add(3, "2"))
      .toThrow(Error("The arguments must be numbers"));
  });
});

파일을 저장하면 테스트가 실패했음을 알 수 있습니다. 좋습니다. 이제 테스트를 작성하고 빨간색으로 바뀌고 녹색이 될 때까지 코드를 작성하는 개발 주기가 있습니다.

이것이 바로 red-green refactor 의 원칙이며, 코드 조각을 안전하게 리팩토링하는 기술입니다. 아마도 빨간색이 될 것이지만 녹색이 되면 리팩터링이 완료됩니다. 이를 통해 애플리케이션이 회귀 없이 여전히 예상대로 작동하고 있음을 알고 코드를 안전하게 이동할 수 있습니다.

테스트를 녹색으로 만들고 계산기에서 유형 검사를 구현해 보겠습니다.

// calculator.js
export default class Calculator {
  _checkArgs(a, b) {
    if (a === undefined || b === undefined) {
      throw new Error("2 arguments are required");
    }
    if (typeof a !== "number" || typeof b !== "number") {
      throw new Error("The arguments must be numbers");
    }
  }
  add(a, b) {
    this._checkArgs(a, b);
    return a + b;
  }
  subtract(a, b) {
    this._checkArgs(a, b);
    return a - b;
  }
}

그들은 다시 녹색이 될 것입니다. \o/

적용 범위 및 테스트 대상

체크 로직을 위임하기 위해 _checkArgs 메소드를 추가했다는 사실을 눈치채셨나요? 아마도 당신은 궁금해 할 것입니다… 그 방법을 테스트해야합니까?

여기에 답이 있습니다. 아니요. 이 메서드는 addsubtract에서 사용하는 도우미 메서드이며 암시적으로 테스트됩니다. add 메소드에 추가한 테스트로 _checkArgs에 대한 테스트 케이스도 테스트됩니다.

커버리지는 얼마나 많은 코드가 테스트되었는지 알기 위해 사용할 수 있는 메트릭입니다. Jest는 Coverage 인수와 함께 실행하여 사용할 수 있는 내장된 Coverage 도구와 함께 제공됩니다.

npm run test -- --coverage

이 명령은 우리에게 현재 100% 커버리지를 제공 합니다:

명령문, 기능 및 행이 명확하다고 생각하지만 제길… 분기란 무엇입니까? 코드의 가능한 실행 경로입니다. 예를 들어 코드에 if 문이 있는 경우 두 개(또는 그 이상)의 경로가 있습니다. if를 입력하는 경로와 그렇지 않은 경로입니다.

커버리지는 테스트를 위한 궁극적인 메트릭이 아니며 실제로 약간의 생리학적 효과가 있음을 명심하십시오. 예를 들어 라이브러리를 만들 때 대상이 개발자인 경우 높은 적용 범위로 좋아할 것입니다.

100% 커버리지가 코드가 깨지지 않는다는 것을 의미하지는 않습니다. 실제로 테스트가 제대로 이루어지지 않았거나 제대로 테스트되지 않은 상태에서도 여전히 100% 적용 범위를 제공할 수 있습니다.

테스트는 장기적으로 시간을 절약하는 반면 작성하는 데도 시간이 걸린다는 점을 명심하십시오. 사실 모든 테스트가 동일한 값을 제공하는 것은 아닙니다. 일부 테스트는 다른 테스트보다 작성하기 어렵고 코드베이스에는 다른 것보다 더 중요한 모듈이 있습니다. 예를 들어 결제 모듈은 테스트에 매우 중요해야 하지만 일부 로깅 도구는 그렇지 않을 수 있습니다.

스프린트에서 무언가를 개발해야 하는 시간, 그 중요성, 제공된 시간을 생각하고 결정을 내립니다. 많은 회사들이 테스트 스위트에 대해 일반적으로 약 70%에서 80%의 최소 적용 범위 규칙을 설정합니다. 이는 의미가 있습니다.

기억하십시오
모든 것이 테스트될 필요는 없으며 실용적으로 존재해야 합니다.

댓글남기기