Frontend/JS.info 정리

코드 품질 - 테스트 자동화

Creative_Lee 2021. 12. 7. 15:41

코드를 테스트 해야하는 이유가 뭘까요?

함수를 하나 만들고 있다고 가정해봅시다!

매개변수도 사용해 보고 이러쿵 저러쿵 만들어 갑니다.

그 과정속에 함수의 실행 결과를 확인하는 작업이 필수로 포함될 것입니다!

내가 만든 함수가 잘 동작하는지, 다양한 인자가 주어졌을 때 케이스마다 잘 동작하는지 확인해야 하기 때문이죠!

 

예시입니다!

내가 만든 함수 num(n)을 테스트 해보려고 합니다.

 

1. num(0)의 동작을 확인 해봅니다.  --> 문제 없이 작동하네요!  다른 인자도 전달해봤습니다.

2. num(1)의 동작을 확인 해봅니다.  --> 이런... 에러가 발생해서 코드를 수정했습니다! 

3. num(1)의 동작을 다시 확인 해봅니다.  --> 문제 없이 작동했습니다! 테스트를 종료합니다!

 

여기서 문제점이 무엇일까요?

2번에서 코드를 수정했기 때문에 num(0)의 결과를 다시 확인해야 합니다!

 

이렇게 코드를 수동으로 테스트하면 에러의 여지를 남길 수밖에 없습니다!

때문에 우리는 자동화 테스트를 도와주는 라이브러리나 프레임워크의 도움을 받는 것이 좋습니다~

 

 

jest 프레임워크 사용해보기!

문서 본문에서는 mocha를 예로들어 테스트 자동화를 설명하고 있지만, 

본문을 읽고 테스트 코드 작성에 대해 검색하던 중 jest를 알게 되었고

이것이 요물이라는 평이 많아서 한번 써보려고 합니다. 

 

 

1. 개발의 순서 ( with Behavior Driven Development  a.k.a BDD 방법론)

 

실제 개발의 순서는 다음과 같습니다.

 

1. 스펙 초안을 작성합니다. 여기에는 기본적인 테스트 케이스가 들어갑니다.

2. 스펙 초안을 보고 코드를 작성합니다.

3. 코드가 잘 작동하는지 확인하기 위해 테스트 프레임워크를 사용해 스팩을 실행합니다.

4. 테스트에 에러가 출력되지 않을 때 까지 코드를 수정합니다.

5. 코드 초안이 완성됩니다!

6. 지금까지 고려하지 않았던 케이스를 스팩에 추가하고 테스트를 계속 이어갑니다.

7. 기능의 완성까지 3~6번을 반복합니다!!  

 

스펙의 용도는 세 가지입니다.

 1. 테스트 - 함수가 의도된 동작을 잘 수행하는 지 보장해 줍니다.

 2. 문서 - 함수의 동작을 설명해 줍니다.

 3. 예시 - 실제 동작의 예시를 통해 함수 사용법을 알려줍니다.

 

여태까지 코드는 그냥 짜면 되는 줄 알았는데.... 쉽지않은 과정이네요...

 

2. jest 사용하기!

 

1. npm 명령어로 jest 프레임워크를 설치합니다.   

   $ npm i -D jest  // 개발 단계에서만 사용하는 패키지 이므로 -D 명령어를 붙여줍니다

 

2. package.json 파일의 "scripts" 를 수정해 줍니다.

"test" : "jest" 로 수정

터미널에 npm test 를 입력했을 때 jest를 실행하겠다 라고 스크립트 커맨드 등록을 한 것입니다.

 

3. 테스트 할 함수를 준비합니다.

sum.js 입니다!!

function sum(a, b) {
    return a + b
}

module.exports = sum   //모듈로 내보냈습니다~

 

4. 테스트를 위한 코드가 담긴 스펙을 만듭니다.

    기본적인 틀은 아래와 같습니다.

describe('구현하고자 하는 기능에 대한 설명이 들어갑니다', () => {
    test('케이스의 대한 설명이 들어갑니다', () => {
        expect(테스트할 함수가 들어갑니다).toXXX(비교 할 값이 들어갑니다.);
    });
})

각 구성요소는 다음과 같이 사용합니다.

 

1. describe( "설명" , callback func ) : 코드가 구현하는 기능에 대한 설명을 적습니다. 

                                                   test케이스를 한곳에 모으는 역할도 합니다.

 

2. test( "설명" , callback func ) : test 케이스에 대한 설명을 적습니다. 이 곳에 테스트 함수가 들어갑니다.

 

3. expect( 테스트 대상 함수 ) : 테스트 대상 함수를 적어줍니다.

 

4. .toXXX : Matcher함수 라고 합니다. 테스트 할 함수의 실행값과 비교값을 어떻게 비교할 것인가 정합니다.

 

*자주 쓰는 Matcher 함수

toBe() : 숫자나 문자와 같은 기본형( Primitive ) 값을 비교할 때 사용합니다

toEqual() : 객체 단위를 비교할 때 사용합니다.

toBeTruthy() , toBeFalsy() : 값이 truthy한지 falsy한지 비교할 때 사용합니다.

toHaveLength() : 배열의 길이를 비교할 때 사용합니다.

toContain() : 배열이 특정 값을 포함하는지 검사할 때 사용합니다.

 

이 외에도 많은 matcher 함수들이 있습니다~

 

위 사용법을 토대로 아래의 스팩을 만들었습니다.

test.js 입니다!


const sum = require('./sum') // sum함수를 받아 왔습니다.

describe('a + b 의 값을 리턴합니다.', () => {
    test('1과 2를 더하면 3입니다.', () => {
        expect(sum(1, 2)).toBe(3);
    });
})

 

 

 

5. test를 실행합니다.

$ npm test

결과를 보면 테스트를 통과했습니다~~~

 

여기까지의 과정이 개발의 순서 1~5 번까지의 내용입니다.

이제 테스트 케이스를 추가해 가면서 스팩을 개선합니다.

 

 

 

스펙 개선하기!

저는 위의 스펙을 이렇게 개선해 봤습니다!

testSum 이라는 함수를 하나 만들고 그 안에 테스트 케이스를 넣었습니다.

for 문이 반복되면서 testSum 함수를 5번 실행합니다.

테스트가 5번 반복됩니다.

결과를 보면 5번의 테스트 모두 성공적으로 통과했습니다!!

 

 

중첩 describe

describe 안에서 또 다른 describe을 만들어 줌으로써 
하위 테스트 그룹을 만들 수 있습니다. 

위에서 만든 헬퍼함수 testSum은 오직 for문에서만 쓰이기 때문에 별도의 그룹으로 나누어 주었습니다.

 

 

 

이후 저는 또 다른 테스트 그룹을 만들어 주었습니다.

테스트를 추가하려구요!

sum 함수의 인자로 음수가 오면 NaN을 리턴한다고 가정하고 테스트 코드를 작성해 봤습니다.

이후 테스트를 실행하면?

당연하게도 테스트에 실패합니다.

실제 sum함수에는 인자의 음수처리에 대한 코드가 아직 없기 때문이죠!

 

 

인자가 음수면 NaN을 return 하도록 코드를 추가했습니다.

테스트를 실행하면 ?

테스트에 성공적으로 통과했습니다!

 

이런 방법으로 스펙을 먼저 작성하고 구현을 시작한다면,

구현이 종료된 시점에는 스펙과 코드 모두 확보할 수 있습니다.

 

이러한 방법을 Behavior Driven Development  

일명 BDD 방법론 이라고 합니다.

 

 

기본이 중요하다