이 글은 내가 우아한테크캠프에서 배웠던 내용들을 복습하기 위해 진행하는 토이프로젝트(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)을 택했다.

+ Recent posts