6. TDD (테스트 주도 개발) - 2
이 글은 내가 우아한테크캠프에서 배웠던 내용들을 복습하기 위해 진행하는 토이프로젝트(https://github.com/minseokLim/woowahan-tech-camp-review)를 중심으로 작성되었다.
1. 단위 테스트란?
단위 테스트는 특정 단위(테스트 대상)가 의도한대로 작동하는지 검증하는 테스트를 말한다. 이때 단위에 대한 정의는 하는 사람마다 조금씩 다를 수 있으나, 소프트웨어 시스템의 작은 부분에 초점을 맞춘 저수준이라는 개념이다. 단위 테스트는 협력 객체를 어떻게 다루는 지에 따라 2가지로 분류할 수 있다.
2. 통합과 고립
단위 테스트는 협력 객체에 실제 객체를 사용하는지(통합), 아니면 가짜 객체를 사용하는지(고립)에 따라 2가지로 분류된다. 아래 예시를 살펴보자. Line(노선)에 대해 Station(역)이 협력객체인 상황이다.
class LineTest {
@Test
void 실제_객체로_테스트() {
final Station 송내역 = new Station("송내역", true);
final Station 신도림역 = new Station("신도림역", true);
final Line 일호선 = new Line("1호선", 송내역, 신도림역);
assertThat(일호선.getOpenedStations()).hasSize(2);
}
@Test
void 가짜_객체로_테스트() {
final Station 송내역 = mock(Station.class);
final Station 신도림역 = mock(Station.class);
when(송내역.isOpened()).thenReturn(true);
when(신도림역.isOpened()).thenReturn(true);
final Line 일호선 = new Line("1호선", 송내역, 신도림역);
assertThat(일호선.getOpenedStations()).hasSize(2);
}
}
public class Line {
private Long id;
private String name;
private List<Station> stations = new ArrayList<>();
public Line(final String name, final Station upStation, final Station downStation) {
this.name = name;
stations.add(upStation);
stations.add(downStation);
}
public List<Station> getOpenedStations() {
return stations.stream()
.filter(Station::isOpened)
.collect(Collectors.toList());
}
}
public class Station {
private Long id;
private String name;
private boolean opened;
public Station(final String name, final boolean opened) {
this.name = name;
this.opened = opened;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public boolean isOpened() {
return opened;
}
}
실제 객체를 사용할 경우
- 실제 객체를 사용할 경우, 협력 객체의 상세 구현에 대해선 알 필요가 없다.
- 하지만 협력 객체의 정상 동작 여부에 해당 단위 테스트의 결과가 영향을 받는다.
가짜 객체를 사용할 경우
- 테스트 대상을 검증할 때, 외부 요인(협력 객체)으로부터 철저히 격리된다.
- 하지만 테스트가 협력 객체의 상세 구현에 의존하게 된다.
3. TDD 접근 방식
TDD 접근 방식에는 Outside In 방식과 Inside Out 방식이 있다.
1) Outside In : 시스템 외부 요청에 대한 테스트부터 작성하며 개발을 시작하는 방식이다. 테스트 코드 작성 시, 협력 객체를 가짜 객체로 만들어 개발을 이어 나간다.
2) Inside Out : 도메인 설계를 한 후, 의존 관계를 갖지 않는 가장 내부의 객체들에 대한 테스트 코드부터 작성해나간다. 테스트 코드 작성이 완료되면, 그 객체를 협력 객체로 가지는 객체에 대한 테스트 코드를 작성함으로써 점점 바깥 방향으로 이어 나간다.
둘 중 어느 것을 선택해야하는 문제가 아니다. 상황에 따라 적절한 방식을 취하도록 한다.
사실은 상향식, 하향식 둘 다 TDD의 프로세스를 효과적으로 설명해 줄 수 없다. 만약 어떤 방향성을 가질 필요가 있다면 '아는 것에서 모르는 것으로(known-to-unknown)' 방향이 유용할 것이다. 우리가 어느 정도의 지식과 경험을 가지고 시작한다는 점, 개발하는 중에 새로운 것을 배우게 될 것임을 예상한다는 점 등을 암시한다.
- Test-Driven Development, kent beck -
4. 구현 방식
이 프로젝트에서는 인수 테스트 코드를 통해 요구사항과 기능 전반에 대한 이해를 선행하고(Outside In), 내부 구현에 대해서는 클래스 설계를 통해 안쪽부터 구현을 해나가는 방식(Inside Out)을 택했다.