[책 정리] 리팩터링
4장. 테스트 구축하기
B#
2024. 1. 1. 17:34
리팩터링은 분명 가치있는 도구지만, 그것만으로는 부족하다.
리팩터링을 제대로 하려면 불가피하게 저지르는 실수를 잡아줄 견고한 테스트 스위트(suite)가 뒷받침되어야 한다.
테스트를 작성하는 것은 장기적으로 봤을 때 개발, 리팩터링 시간을 줄여준다.
테스트 작성 시 효율이 좋아지는 이유
- 자가 테스트 코드의 가치
- 테스트 코드 자체 + 자주 테스트 실행 = 버그 금방 찾기
- 회귀 버그가 상당히 줄게 됨
- 회귀 버그 (regression bug) : 잘 작동하던 기능에서 문제가 생기는 현상
- 올해는 개발하고 JUnit 사용해보자 ★
- 저자는 프로그래밍 시작 전에 테스트를 작성한다고 함.
- 뭔가 뒤바뀐 거 같지만, 테스트를 작성하다보면 원하는 기능을 추가하기 위해 무엇이 필요한지 고민하게 되고 코딩이 완료되는 시점을 정확하게 알 수 있게 된다고 함 (테스트를 모두 통과한 시점)
- 테스트주도개발(TDD, Test-Driven Development)
- 테스트할 샘플 코드
- 첫 번째 테스트
- 테스트 결과를 빠르게 확인할 수 있는 테스트 프레임워크 사용
- GUI 테스트 러너를 제공하는 IDE 사용하면 빨리 확인 가능
- JUnit으로 TDD 실천해보기
- 테스트 추가하기
- 테스트는 위험요인을 중심으로 작성해야 한다.
- 모든 public 메서드를 빠짐없이 테스트 = 의미없음
- 테스트를 너무 많이 만들다보면 오히려 필요한 테스트를 놓치기 쉽다.
- 잘못될까봐 가장 걱정되는 영역을 집중적으로 테스트
- 테스트 코드에서도 중복은 피하자
- 테스트 관련 버그 중 가장 지저분한 유형인 '테스트끼리 상호작용하게 하는 공유 픽스처'를 생성하는 것
describe('province', function() { const asia = new Province(sampleProvinceData()); // 이렇게 하면 안 된다. it('shortfall', function() { expect(asia.shortfall).equal(5); }); it('profit', function() { expect(asia.profit).equal(230); }); });
- 위와 같이 작성하면 const 키워드는 asia 객체의 '내용'이 아니라 'asia를 가리키는 참조(주소값)'가 상수임을 뜻함.
- 나중에 다른 테스트에서 이 공유 객체의 값을 수정하면 이 픽스처를 사용하는 또 다른 테스트가 실패할 수 있다.
- 이는 테스트 실행 순서에 따라 결과가 달라질 수 있다는 말이고, 테스트 결과가 제멋대로가 되어 버그 잡기가 힘들다.
- 아래와 같이 매 테스트마다 초기화해주는 것이 안전하다.
describe('province', function() { let asia; beforeEach(function(){ // JUnit에도 @BeforeEach 어노테이션 사용하여 초기화 가능 asia = new Province(sampleProvinceData()); }); it('shortfall', function() { expect(asia.shortfall).equal(5); }); it('profit', function() { expect(asia.profit).equal(230); }); });
- 픽스처 수정하기
- 준비-수행-단언 (arrange-act-assert) / 설정-실행-검증 (setup-exercise-verify) / 조건-발생-결과 (given-when-then) /
- 준비 (arrange)
- 테스트 수행을 위해 필요한 초기 상태 설정
- 필요한 객체 생성, 의존성 주입, 데이터 준비 등의 작업 - 실행, 수행 (act)
- 테스트하려는 코드를 호출, 실행
- 검증하려는 특정 메서드나 기능을 호출 - 단언 (assert)
- 실행한 코드의 결과를 기대하는 값과 비교하여 검증
- 경계 조건 검사하기
- 지금까지는 모든 일이 순조롭고 사용자도 개발자의 의도대로 사용하는 상황에만 집중했다면,
이 범위를 벗어나는 경계 지점에서 문제가 생기면 어떤 일이 벌어지는지 확인하는 테스트도 함께 작성하면 좋다. - 컬렉션 -> 빈 컬렉션일 때의 테스트
- 정수 -> 0일 때, 음수일 때, 문자일 때, 빈 값일 때
- 지금까지는 모든 일이 순조롭고 사용자도 개발자의 의도대로 사용하는 상황에만 집중했다면,
- 끝나지 않은 여정
- 제품 코드 보강 + 테스트 코드 보강
- 기능 새로 추가 + 테스트 새로 추가
- 버그 발견 + 버그 잡아내는 테스트
[마틴 파울러 - 리팩터링] 발췌