2장 테스팅과 디버깅 갖추기

유웅조·2022년 1월 30일
0

2.1 코드 디버깅

2.1.1 로깅

console.log ⇒ console을 보는 다양한 방법

로깅은 코드가 동작하고 있는 동안, 어떤 상태에 있는지를 보는 용도로는 정말 좋다. 그러나 동작을 중단시키고 해당 상태를 보기 원한다면 중단점(breakpoint)을 사용해야 한다.

+) Use console.log() like a pro

Use console.log() like a pro

2.1.2 중단점 breakpoint

코드의 실행이 중지된 시점에서, 모든 상태를 좀더 여유롭게 조사할 수 있게 해준다. 중지된 상태에서 조사할 수 있는 상태는 중지한 위치에서 접근할 수 있는 모든 변수들과 콘텍스트, 유효 범위를 포함한다.

VSCode에서도 디버깅 도구를 사용해서 breakpoint를 찍고 그에 따른 환경들을 확인할 수 있다

Debugging in Visual Studio Code

console.log("hello world!");
const factorial = (num) => {
  if (num === 0 || num === 1) return 1;
  return num * factorial(num - 1);
};
const result = factorial(6);
console.log(`result is...${result}`);
console.log("bye world!");

2.2 테스트 생성

좋은 테스트의 3가지 조건

1) 반복성: 테스트 결과는 항상 재현 가능해야 한다.

2) 간결성: 테스트는 테스트를 하는 것에만 집중해야 한다.

3) 독립성: 각 테스트는 독립적으로 동작해야 한다.

테스트를 만드는 방법

1) 제거적 테스트 케이스: 문제를 격리하기 위해 테스트와는 상관 없는 모든 것을 제거하여 기존의 코드가 줄어들면, 제거적 테스트 케이스가 만들어진다.

2) 추가적 테스트 케이스: 올바르다고 알고 있는, 작은 테스트 케이스를 가지고 확인하려는 버그가 재현될 때까지 테스트 케이스를 늘려 나간다.

테스트의 3가지 타입

Unit Test | 단위 테스트

함수나 클래스처럼 독립적인 단위의 작은 유닛을 테스트하는 것. 만약 테스트가 외부 리소스(네트워크나 DB)를 사용한다면 유닛 테스트가 아니다.

Integration Test | 통합 테스트

여러 유닛을 테스트하는 것. 개발자가 의도한 대로 동작하는지 테스트하는 것.

End to End Test | E2E 테스트 (혹은 Functional Test 기능 테스트)

유저 입장에서 유저가 사용하는 기능을 처음부터 끝까지 테스트하는 것. 유저가 직접 애플리케이션을 사용하는 것처럼 동작하도록 스크립트를 작성하고, 실제로 실행시켜보면서 테스트하는 방식.

사례) 리액트 컴포넌트 테스팅 (출처)

  • 특정 props 에 따라 컴포넌트가 크래쉬 없이 잘 렌더링이 되는지 확인
  • 이전에 렌더링했던 결과와, 지금 렌더링한 결과가 일치하는지 확인
  • 특정 DOM 이벤트를 시뮬레이트 하여, 원하는 변화가 제대로 발생하는지 확인
  • 렌더링된 결과물을 이미지 로 저장을 하여 픽셀을 하나하나 확인해서 모두 일치하는지 확인

2.3 테스트 프레임워크

2019년 기준 Jest, Mocha, Sinon, Karma, Jasmine 순으로 많이 쓰인다. (이외에도 Pupperteer, Enzyme 등)

(출처: https://javascript.plainenglish.io/3-things-i-learned-from-testing-in-javascript-94f295db2df7)

리액트의 테스트 Tool

Jest와 Enzyme이 유명하고 가장 많이 사용된다. Jest는 테스트 프레임워크고, Enzyme은 테스트 라이브러리. 둘다 동시에 사용 가능하다.

Jest는 페이스북에서 Jasmine 기반으로 만든 테스팅 프레임워크이며, CRA로 만든 리액트 프로젝트에는 자동으로 적용이 되어있다.

2.4 테스트 스위트의 기본

테스트 스위트의 주된 목적은 개별 테스트를 묶어 하나의 자원으로 제공함으로써 여러 테스트를 한 번에 실행할 수 있게 그리고 반복해서 간단히 실행할 수 있게 하는 것이다. (특히 비동기 테스트가 어떻게 동작하는지를 보면 도움이 됨)

2.4.1 검증 조건

단위 테스트 프레임워크의 핵심은 검증 메서드로, 이 메서드의 이름은 일반적으로 assert()다. 해당 메서드는 항상 검증의 전제가 되는 표현 하나와 검증의 목적을 설명하는 인자를 받는다.

/* 본문의 그 코드 */
const assert = (value, desc) => {
  let li = document.createElement("li");
  li.className = value ? "pass" : "fail";
  li.appendChild(document.createTextNode(desc));
  document.getElementById("results").appendChild(li);
};

window.onload = () => {
  assert(true, "The test suite is running");
  assert(false, "Fail");
};

2.4.2 테스트 그룹

간단한 검증 조건만으로도 유용하지만, 테스트 내용에 따라서 테스트를 그룹으로 묶을 때 정말 유용해진다.

let results;
const assert = (value, desc) => {
  const li = document.createElement("li");
  li.className = value ? "pass" : "fail";
  li.appendChild(document.createTextNode(desc));
  results.appendChild(li);

  if (!value) li.parentNode.parentNode.className = "fail";
  return li;
};

const test = (name, fn) => {
  results = document.getElementById("results");
  results = assert(true, name).appendChild(document.createElement("ul"));
  fn();
};

window.onload = () => {
  test("A test", () => {
    assert(true, "first assertion completed");
    assert(true, "Second assertion completed");
    assert(true, "third assertion completed");
  });

  test("B test", () => {
    assert(true, "first test completed");
    assert(false, "second test failed");
    assert(true, "third assertion completed");
  });

  test("C test", () => {
    assert(null, "fail");
    assert(5, "pass");
  });
};

2.4.3 비동기 테스트

비동기 테스트를 다루려면 다음 단계를 따라야 한다.

1) 동일한 비동기 연산에서 사용해야 하는 검증 조건은 같은 테스트 그룹으로 묶어야 한다.

2) 각 테스트 그룹은 하나의 큐에 존재해야 하고, 이전 테스트 그룹이 모두 종료한 뒤에 실행되어야 한다.

const queue = [];
let paused = false,
  results;

const runTest = () => {
  if (!paused && queue.length) {
    queue.shift()();
    if (!paused) {
      resume();
    }
  }
};

const assert = (value, desc) => {
  const li = document.createElement("li");
  li.className = value ? "pass" : "fail";
  li.appendChild(document.createTextNode(desc));
  results.appendChild(li);
  if (!value) {
    li.parentNode.parentNode.className = "fail";
  }
  return li;
};

const test = (name, fn) => {
  queue.push(() => {
    results = document.getElementById("results");
    results = assert(true, name).appendChild(document.createElement("ul"));
    fn();
  });
  runTest();
};
const pause = () => {
  paused = true;
};

const resume = () => {
  paused = false;
  setTimeout(runTest, 1);
};

window.onload = () => {
  test("async test #1", () => {
    pause();
    setTimeout(() => {
      assert(true, "first test completed");
      resume();
    }, 1000);
  });

  test("async test #2", () => {
    pause();
    setTimeout(() => {
      assert(false, "second test completed");
      resume();
    }, 1000);
  });
};

키포인트는 하나의 비동기 처리를 한 덩어리로 묶어서
그것이 다 마쳐졌을 때 테스트를 마치고 다음 테스트를 실행한다던가 하는것임~

2.5 정리

자바스크립트 코드 디버깅과 간단한 테스트 케이스 구축에 대해 다음과 같은 것들을 살펴보았다.

  • 코드가 실행 중일 때의 동작을 확인하기 위해 어떻게 로깅을 사용할 수 있는지 테스트.
  • 실행을 잠시 중지시키고 그 시점의 상태를 살펴볼 수 있도록 중단점을 어떻게 사용하는지.
  • 테스트 생성법과 좋은 테스트의 속성인 '반복성, 간결성, 독립성'을 정의, 테스트의 두 가지 큰 형태인 '제거적 테스트'와 '추가적 테스트'에 대해 살펴봄
  • 자바스크립트 커뮤니티에서 어떻게 테스트를 하는가에 대한 자료.
  • 테스트 프레임워크를 만들기 위해 검증 조건에 대한 개념 소개.
  • 비동기 테스트 케이스를 다룰 수 있는 테스트 스위트를 어떻게 구축하는지.

참고자료

Top 10 JavaScript Testing and Debugging Tools in 2019 https://medium.datadriveninvestor.com/top-10-javascript-testing-and-debugging-tools-in-2019-76862733b4b2

Jest | 벨로퍼트와 함께하는 리액트 테스팅 https://velog.io/@velopert/자바스크립트-테스팅의-기초

React Testing 시리즈 https://jbee.io/react/testing-0-react-testing-intro/

함께한 사람들: edie.spark1ler, jun.choi.4928, guswnl0610

0개의 댓글

관련 채용 정보