TestDouble이란?

단위 테스트를 진행하기 어려운 경우 이를 대신해 테스트를 진행할 수 있도록 만들어주는 객체를 의미한다.

테스트 자동화에서 여러 객체들이 의존성을 가지고 있는 경우 테스트하는데 어려움이 있다. 예를 들어 고객정보를 조회하는 API는 Database에 의존한다. 이런 경우 Database의 상태에 따라 테스트 결과가 달라질 수 있다. 반면에 의존 관계가 간단한 경우 테스트 대상에만 집중할 수 있다. TestDouble은 이런 의존관계를 줄여주는 역할을 한다.

TestDouble의 종류

TastDouble은 크게 Dummy, Fake, Stub, Spy, Mock 5가지로 나뉜다.

Dummy는 가장 기본적인 TestDouble로 인스턴스화 된 객체가 필요하지만 기능은 필요하지 않을 경우 사용한다. Dummy 객체의 메서드는 보통 아무런 동작을 하지 않는다. 아래 코드는 Dummy TestDouble의 샘플 소스 이다.

public class PrintWarningDummy implements PrintWarning {
    @Override
    public void print() {
        // 아무런 동작을 하지 않는다.
    }
}

Fake는 복잡한 로직이나 객체 내부에서 필요로 하는 다른 외부 객체들의 동작을 단순화하여 구현한 객체이다. 동작의 구현은 필요하지만, 실제, 프로덕션에는 영향을 주지 않아야 할 때, Fake를 사용한다. 아래 소스코드로 Database 관련 테스트에 적용해보자.

public class FakeUserRepository implements UserRepository {
    private Collection<User> users = new ArrayList<>();
    
    @Override
    public void save(User user) {
        if (findById(user.getId()) ** null) {
            user.add(user);
        }
    }
    
    @Override
    public User findById(long id) {
        for (User user : users) {
            if (user.getId() ** id) {
                return user;
            }
        }
        return null;
    }
}

Stub는 객체가 실제 동작하는 것처럼 보이게 만들어 놓은 객체를 뜻한다. 호출 시, 입력에 상관없이 미리 정해진 결과를 반환하며, 일반적으로 매우 간단한 형태를 갖는다.

public class StubUserRepository implements UserRepository {
    @Override
    public User findById(long id) {
        return new User(id, "Test User");
    }
}

SpyStub + 호출된 내용에 대한 약간의 정보를 기록하는 객체로, 특정 객체의 동작을 관찰할때 사용하면 좋다.

public class MailingService {
    private int sendMailCount = 0;
    private Collection<Mail> mails = new ArrayList<>();

    public void sendMail(Mail mail) {
        sendMailCount++;
        mails.add(mail);
    }

    public long getSendMailCount() {
        return sendMailCount;
    }
}

Mock호출에 대한 기대를 명세하고 내용에 따라 동작하도록 프로그래밍 된 객체이다. Mock은 외부 의존성을 완전히 제거하고, 실제 객체를 완전히 대체할 수 있는 수단이다.

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
    @Mock
    private UserRepository userRepository;
    
    @Test
    void test() {
        when(userRepository.findById(anyLong())).thenReturn(new User(1, "Test User"));
        
        User actual = userService.findById(1);
        assertThat(actual.getId()).isEqualTo(1);
        assertThat(actual.getName()).isEqualTo("Test User");
    }
}

연결문서

댓글남기기