08.06 TIL

이형우·2020년 8월 6일

Test 주도 개발
Test Framework 이란..?
Unit Test 를 돕는 Tool

학습 목표

  • mocha 에서 사용하는 describe와 it의 용도를 이해할 수 있다.
  • chai 에서 사용하는 assert, expect의 용도를 이해할 수 있다.
  • 유닛 테스트(테스트 케이스)와 테스트 주도 개발이 무엇인지 이해할 수 있다.
    -> 유닛 테스트 : 경우의 수를 정리해서 하나씩 기대값과 실제값을 비교하는 것
    -> 테스트 주도 개발 : 이란 개발 방법론을 테스트 주도 개발이라 부름
  • Test Framework가 어떤 역할을 하는지 이해할 수 있다.
  • 코드를 작성하기 전에 무엇이 올바른 코드인지 정의하고 그에 맞는 유닛 테스트 코드를 먼저 작성하여 개발할 수 있다.
  • 바람직한 코드가 가져야 할 덕목을 유닛 테스트를 작성한다. (make it fail)
  • 테스트를 통과시킨다. (make test pass)
  • 바람직한 코드가 될 수 있도록 재구성한다. (refactor)
  • 다시 유닛 테스트를 작성한다. (make it fail, again)
  • JavaScript에서 사용되는 Test Framework가 무엇이고, 장단을 이해할 수 있다.
    mocha, chai, jest, should, supertest ...

테스트 주도 개발
Overview

TDD(Test-driven Development)는 코드를 작성하기 전에 테스트를 쓰는 방법론. 개발자 자신이 바람직하다고 생각하는 코드의 결과를 미리 정의하고, 이것을 바탕으로 코드를 작성하는 법.
1/ 먼저 개발자는 몇 가지 테스트를 작성한다. - Add Tests
2/ 개발자는 그런 테스트를 실행하고 실제로 테스트할 구현된 기능이 없으므로 당연히 실패한다. - See Tests Fall
3/ 다음으로 개발자는 코드에서 이러한 테스트를 실제로 구현한다. - Write Code
4/ 개발자가 코드를 잘 작성하면 다음 단계에서 테스트가 통과하는 것을 볼수 있다. - Run Tests
5/ 개발자는 새로운 코드가 무언가를 망치면 테스트가 실패할 것이라는 것을 알고 있기 때문에 원하는대로 코드를 리펙토링하고 주석을 추가하고 정리할수 있다. - Refactor

테스트 코드를 작성하고 테스트로 검증된 코드를 가지고 실제코드를 작성하는 방식.
개발을 먼저하고 테스트를 하는 기존의 방식이 아닌 테스트 코드를 작성하고 검증된 코드를 실제 코드로 반영하자는 개념.

Pros
실제 코드보다 테스트를 먼저 작성한다는 것은, 이미 내가 바람직한 코드가 무엇이고, 어떻게 작성해야 되는지에 대한 고민이 이미 끝났다는 의미. 즉, 테스트를 먼저 작성하는 개발은 필연적으로 코드를 어떻게 구성할지 고민하고, 그 과정에서 버그가 더 적은 코드를 짜게 된다. 테스트가 쉽도록 코드의 구조를 기획(Design)하는 것도 같은 효과를 내게 된다.

Cons
TDD는 프로그래머들이 자연스럽게 생각하고 일하는 방식과 일치하지 않는다. 소프트웨어는 집을 건축하는 것과는 달리 더 유동적이다. 아마 대부분의 프로그래머는 테스트를 작성하는 것보다 바로 뭔가 만들어내고 싶어 할 것임.

같은 맥락에서 또 살펴보면, 코드를 기획한다는 것은 초반에 명확하지 않을 수 있고 대신 빠르게 프로토타입을 만들어 전반적인 아웃라인을 그리는 방법이 나을 수 있다. 이런 경우, 기획이 지속적으로 바뀌는 과정에서 초반에 테스트를 짜다가 이후에 다시 짜야 하거나 지워야 할 경우가 높기 때문에 시간 낭비라고 느껴질 수도 있다.

지금까지 살펴본 단점들을 요약한 가장 큰 단점이라고 하면 바로 속도. 장기적으로 피할 수 있는 문제들을 피할 수 있다는 점에서 반론의 여부가 있을 수 있겠지만 속도가 느리다는 생각이 기본 정설.

Popularity
위에 살펴본 단점들 때문에 완전한 TDD를 따르는 사람들은 적다. 그럼에도 자동화된 테스트를 작성하는 것은 기본이며, 어느정도 프로젝트가 완성되고 나면 테스트를 작성하게 된다. 탄탄한 프로그래밍 팀은 테스트를 포함하지 않은 코드에 대한 pull request 요청이 들어왔을 때, 정말 사소한 부분이 아니고서는 merge하지 않는다.

What is Test Framework?
Test Framework 의 구성 요소

유닛 테스트
각각의 컴포넌트들이 서로 의도대로 작동하는지 확인하기 위함.
Ex)
-입력창에 알파벳을 넣을 수 있어야 합니다. 'abc'
-버튼을 클릭할 수 있어야 합니다.
-버튼을 클릭하면 입력창의 내용이 서버로 전송되어야 합니다.
-서버로부터 대문자로 변경된 내용을 응답 받아야 합니다. 'ABC'

Assertion(주장)
유닛 테스트는 개발자가 생각하기에 "코드는 이렇게 짜야해"라는 Assertion(주장) 코드를 작성하며 시작한다. 테스트를 처음 구성할 때 console.log 를 사용하여 유닛 테스트를 하는 것도 좋으나, Assertion 을 기반으로 한 프레임워크를 잘 사용하여 빠른 테스트를 진행하는 것이 better skill.

Assertion의 예

var output = square(5);
expect(output).to.equal(25);

즉, square 함수의 인자를 5로 입력하여 리턴되는 값이 25가 되어야 한다는 Assertion(주장). 만약 내가 작성한 square 함수에 오류가 있다면, 5를 리턴해야 하는 개발자의 주장과 실제 작성한 코드의 결과가 일치하지 않았기 때문에, 테스트가 통과되지 않은 것.

Matcher

expect(output).to.equal(25);

상기 예시의 뒷 부분 to.equal을 Matcher라고 부른다. 우리가 주장하는 바와 실제 코드 실행 결과가 어때야 하는지에 대한 정보를 담고 있으며, 여기서는 "같아야" 한다 로 사용됨.

Mocha Test
Mocha 테스트는 다음 기능을 하는 도구.

  • 함수를 실행할 때 오류가 발생하면, 실패.
  • 오류가 발생하지 않으면, 실패하지 않는다. 그저 통과.
  • BDD(Behavior-driven development) 스타일을 기본으로 하고 있지만, TDD 스타일도 지원. BDD은 문장을 쉽게 가독할수 있고, 때문에 테스트를 문장과 같이 읽을수 있어 자연스럽고 좀더 포괄적으로 테스트를 할수 있다
  • describe 의 용도
    -> 구현하고자 하는 기능에 대한 설명이 들어간다. Title 만 넣어도 된다. 예를 들어 거듭제곱을 구할 때 거듭제곱 함수를 의미하는 "pow" 를 title 로 넣어 주는 식으로..
    -> it 블록을 한데 모아주는 역할도 함.
describe("title", function() {...})
describe("pow", finction() { // 예시
  • it 의 용도
    -> it의 첫 번째 인수에는 '특정 use case에 대한 설명'이 들어간다. 이 설명은 누구나 읽고 이해할 수 있는 자연어로 적어 준다. 두 번째 인수에는 use case test 함수가 들어간다.
it("use case 설명", function() {...})
it("has a prefix of 39 and a length of 14", function() { // 예시
  • Please see below example codes.
Ex.1
it("예상 동작이 실제 동작과 일치하지 않을 때 오류가 발생합니다.", function() {
    let even = function(num) {
      return num%2 === 0; 
    };

    if (even(10) !== true) {
      throw new Error("10은 짝수여야 합니다!");
    }
  });
});
Ex.2
describe("American Express", function() {
  // 항상 if/throw 구문으로 오류를 체크하는 것은 귀찮은 일이기 때문에,
  // 여기에 도움을 줄 수 있는 함수를 하나 제공했습니다. 입력값이 true가 아닐 경우 에러를 발생시킵니다.
  let assert = function(isTrue) {
    if (!isTrue) {
      throw new Error("Test failed");
    }
  };

  it("has a prefix of 34 and a length of 15", function() {
    assert(detectNetwork("343456789012345") === "American Express");
  });

Chai

  • Chai는 테스트에 필요한 헬퍼 함수들이 담긴 라이브러리.
  • Chai는 이전에 만들었던 assert 함수(Mocha)와 동일한 기능을 하는 assert 함수를 제공.
  • Please refer to below link.
    https://www.chaijs.com/guide/styles/#assert
    Ex - assert
describe("Visa", function() {
let assert = chai.assert;

  it("has a prefix of 4 and a length of 13", function() {
    assert(detectNetwork("4123456789012") === "Visa");
  });
});
  • Chai는 좀 더 영어 문법에 가까운 코드로 테스트를 작성할 수 있게 도와줌.
  • Expect 문법은 그 중 한가지이며, 다른 문법도 있다.
  • Please refer to below link.
    https://www.chaijs.com/api/bdd/
    EX - expect
describe("MasterCard", function() {
let expect = chai.expect;

  it("has a prefix of 51 and a length of 16", function() {
    expect(detectNetwork("5112345678901234")).to.equal("MasterCard");
  });
  • expect 대신에 should라는 문법을 사용해서 스타일 변경 가능
  • 무엇을 쓰던 한가지를 일관되게 사용하는 것이 중요.
  • 만약 테스트를 작성하는 중에, 두가지 방법을 동시에 사용하려고 하면 진행되지 않을 것. 따라서 둘 중 하나만 사용하기.
  • Please refer to below link.
    https://www.chaijs.com/guide/styles/
    EX - should
describe("Discover", function() {

  it("has a prefix of 6011 and a length of 16", function() {
    detectNetwork("6011345678901234").should.equal("Discover");
  });
});
profile
완전 완전 초짜

0개의 댓글