JavaScript

[리팩터링 2판 : 4장] 테스트 구축하기

ㅇㄱ9 2022. 1. 24. 19:30
728x90

해당 내용은 "리팩터링 2판(마틴 파울러)"를 읽고 정리한 글입니다.


리팩터링을 제대로 하려면 견고한 테스트가 필요하다. 

4.1 자가 테스트 코드의 가치

"모든 테스트를 완전히 자동화하고 그 결과까지 스스로 검사하게 만들자"

컴파일 시 테스트가 함께 돌아가면서 디버깅 시간이 크게 줄어들었다. 테스트를 몇 분 간격으로 자주 수행했기 때문에 버그가 발생한 지점을 쉽게 찾을 수 있고 의심되는 코드의 양이 많지 않았다. 

 

*회귀 버그 

: 잘 동작하던 기능에서 문제가 생기는 현상. 일반적으로 프로그램을 변경하는 중 뜻하지 않게 발생한다. 같은 맥락에서, 잘 동작하던 기능이 여전히 잘 동작하는지 확인하는 테스트를 회귀 테스트(regression test)라 한다. 

 

"테스트 스위트는 강력한 버그 검출 도구로, 버그를 찾는 데 걸리는 시간을 대폭 줄여준다."

테스트를 작성하기 가장 좋은 시점은 프로그래밍을 시작하기 전이다. 테스트를 작성하다 보면 원하는 기능을 추가하기 위해 무엇이 필요한지 고민하게 되고 구현보다 인터페이스에 집중하게 되는 장점도 있다. 

 

이처럼  테스트부터 작성하는 습관을 바탕으로 창시된 기법이 TDD(Test-Driven development)이고 이는 (처음에는 통과하지 못할) 테스트를 작성하고, 이 테스트를 통과하게끔 코드를 작성하고, 결과 코드를 최대한 깔끔하게 리팩터링 하는 과정을 짧은 주기로 반복한다. 

 

4.2 테스트 할 샘플 코드

코드는 성격에 따라 분리하자 (ex. UI코드 와 비즈니스 로직 코드)

 

4.3 첫번째 테스트

코드를 테스트하기 위해서는 먼저 테스트 프레임워크가 필요하다. ex) Jest, Mocha, Jasmine

(참고 : 자바스크립트 테스트 프레임 워크 간단 비교 )

 

Mocha프레임워크는 테스트 코드를 블록 단위로 나눠서 각 블록에 테스트 스위트를 담는 구조이다. 테스트는 it블록에 담긴다. 

1) 테스트에 필요한 데이터와 객체를 뜻하는 픽스처(fixture)를 설정한다. 

2) 이 픽스처의 속성들을 검증한다. 

"실패해야 할 상황에서는 반드시 실패하게 만들자."

기존 코드를 검증하는 테스트 작성 후 문제없이 통과했으나 불안하다면 일시적으로 코드에 오류를 주입해 각각의 테스트가 실패하는 모습을 한 번씩은 확인해본다. 

 

"자주 테스트하라. 작성 중인 코드는 최소한 몇 분 간경으로 테스트하고, 적어도 하루에 한 번은 전체 테스트를 돌려보자."

모카 프레임 워크는 소위 어서션(assertion)라이브러리라고 하는 픽스터 검증 라이브러리를 선택해 사용할 수 있다. 

assertion은 기대값(expect)과 실제 값(actual)이 일치하는지 확인하는 데 사용된다. 

(참고 : 10 Best JavaScript Assertion Libraries)

 

*Jest의 경우에는 다른 테스팅 프레임워크와 다르게 자체적으로 assertion,mocking라이브러리와 테스트 러너를 가지고 있기 때문에 mock, spy 또는 assertion을 위한 추가적인 라이브러리를 설치할 필요가 없다. 

 

"실패한 테스트가 하나라도 있으면 리팩터링하면 안 된다. "

 

4.4 테스트 추가하기

테스트의 목적은 어디까지나 현재 혹은 향후에 발생하는 버그를 찾는 데 있기 때문에 위험요인을 중심으로 작성해야 한다. 

 

"완벽하게 만드느라 테스트를 수행하지 못하느니, 불완전한 테스트라도 작성해 실행하는 게 낫다."

테스트를 실행하는 순서에 따라 결과가 달라지게 되면 테스트 결과가 제멋대로가 되어 버그를 잡기 어렵고 오래 걸린다. 

예를 들면 아래와 같은 코드에서는 const키워드가 asia객체의 '내용'이 아니라 asia를 가리키는 '참조'가 상수임을 뜻하기 때문에 나중에 다른 테스트에서 이 공유 객체의 값을 수정하면 이 픽스처를 사용하는 또 다른 테스트가 실패할 수 있다. 

const asia = new Province(sampleProvinceData());

beforeEach구문은 각각의 테스트 바로전에 실행되어 asia를 초기화하기 때문에 모든 테스트가 자신만의 새로운 asia를 사용하게 된다. 

let asia;
beforeEach(function(){
	asia = new Province(sampleProvinceData());
})

이처럼 개별 테스트를 실행할때마다 픽스처를 새로 만들면 모든 테스트를 독립적으로 구성할 수 있다. 

 

4.5 픽스처 수정하기

실전에서는 사용자가 값을 변경하면서 픽스터의 내용도 수정되는 경우가 흔하다. 

이러한 수정 대부분은 세터에서 이뤄지는데, 세터는 보통 아주 단순하여 버그가 생길일도 별로 없으니 잘 테스트하지 않는다. 

 

하지만 세터가 복잡한 동작을 수행하는 경우 테스트해볼 필요가 있는데 beforeEach블록에서 '설정'한표준 픽스처를 취해서 테스트를 '수행'하고 이 픽스터가 일을 기대한 대로 처리했는지를 '검증'한다. 

이 패턴을 '설정-실행-검증(setup-exercise-verify)', '조건-발생-결과(given-when-then)', '준비-수행-단언(arrange-act-assert)'등으로 부른다.

 

4.6 경계 조건 검사하기

"문제가 생길 가능성이 있는 경계 조건을 생각해보고 그 부분을 집중적으로 테스트하자"

모카는 숫자 기대값이 숫자일 때 문자열이 들어오면 실패로 처리한다. 하지만 이 와달리 에러와 실패를 구분하는 테스트 프레임워크도 많다. 

실패(failure)란 검증 단게레서 실제 값이 예상 범위를 벗어났다는 뜻이다. 

 

"어차피 모든 버그를 잡아낼 수는 없다고 생각하여 테스트를 작성하지 않는다면 대다수의 버그를 잡을 수 있는 기회를 날리는 셈이다."

 

4.7 끝나지 않은 여정

"버그 리포트를 받으면 가장 먼저 그버그를 드러내는 단위 테스트부터 작성하자."
728x90
반응형