Test Double

Bong2·2022년 4월 4일
0

Test Double이란?

데이터베이스로부터 조회한 값을 연산하는 비즈니스로직을 구현했다고 하면 해당 로직을 테스트하기 위해선 항상 데이터베이스의 영향을 받을 것이고, 이는 데이터베이스의 상태에 따라 다른 결과를 유발할 수 있다.

-> 객체와 연관된 객체를 사용하기가 어렵고 모호할 때 대신해 줄 수 있는 객체를 Test Double이라고 한다.

Test Double의 종류

크게 Dummy, Fake, Stub, Spy, Mock으로 나눠진다.

1. Dummy

  • 가장 기본적인 테스트 더블
  • 인스턴스화된 객체가 필요하지만 기능은 필요하지 않는 경우
  • 객체는 전달되지만 사용되지 않는 객체
public interface AccountDao{
	void showMember();
}

public class AccountDaoDummy implements AccountDao{
	@Override
    public void showMember{
    //Dummy
    }
}

AccountDaoDummy는 아무런 기능을 하지 않고 반환값도 없다. Clinet입장에서는 Dummy를 호출하기만 하면 되는 테스트이므로 내부 동작과는 상관없이 테스트 성공함

2. Fake

  • 실제로 사용된 객체는 아니지만 같은 동작을 하는 구현된 객체
  • 객체의 단순화된 버전
  • 동작의 구현은 되어있지만 프로덕션에서 사용할 수 는 없는 객체

LoginService를 테스트하기 위해서는 AccountDao가 필요하다. 하지만 아직 구현되지 않았거나 독립적으로 미리 테스트하고 싶다면 Fake객체를 사용해서 FakeAccountDao객체는 임의의 Map를 생성하여 진짜인 것처럼 Service가 보낸 메세지에 반응한다.

public class FakeAccountRepository implements AccountRepository {
       
       Map<User, Account> accounts = new HashMap<>();
       
       public FakeAccountRepository() {
              this.accounts.put(new User("john@bmail.com"), new UserAccount());
              this.accounts.put(new User("boby@bmail.com"), new AdminAccount());
       }
       
       String getPasswordHash(User user) {
              return accounts.get(user).getPasswordHash();
       }
}

실제 데이터베이스 설계를 연기하면서 테스트를 신속하게 실행

Stub

  • Dummy 데이터가 실제로 동작하도록 만들어둔 객체
  • 인터페이스 또는 기본 클래스가 최소한으로 구현된 상태
  • 테스트에서 호출된 요청에 대해 미리 준비해둔 결과를 제공
  @Before
    public void setUp() throws Exception {
        gradebook = mock(Gradebook.class);
        student = new Student();
    }

    @Test
    public void calculates_grades_average_for_student() {
        when(gradebook.gradesFor(student)).thenReturn(grades(8, 6, 10)); //stubbing gradebook
        double averageGrades = new GradesService(gradebook).averageGrades(student);
        assertThat(averageGrades).isEqualTo(8.0);
    }

실제 객체 대신 Stub(when을 이용하여 반환할 데이터를 미리 정의)을 도입하고 어떤 데이터를 반환해야 하는지 정의 후 리턴

Spy

  • Stub의 역할을 가지면서 호출된 내용에 대한 약간의 정보를 기록
  • 자기 자신이 호출되었을 때 확인이 필요한 부분을 기록
  • 실제 객체처럼 동작시킬 수도 있고, 필요한 부분에 대해서 Stub로 만들어서 동작을 지정할 수 있다.
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;
    }
}

Mockito 프레임워크의 verify()메서드가 같은 역할을 한다.

Mock

  • 행위를 검증하기 위해 가짜 객체를 만들고 테스트하는 방법
  • Stub 상태를 검증할 때 사용
    @Test
    public void mockTest(){
        List mockedList = mock(List.class);
	
    	// 행위 추가
        mockedList.add("one");
        mockedList.clear();

		// 행위 검증
        verify(mockedList).add("one");
        verify(mockedList).clear();
    }

정리

테스트를 진행한다면 위의 항목들 중에서 어떤 테스트 더블이 필요할지 결정하고, 결정한 테스트가 경계를 벗어나는지 보며 가능한 최소한의 테스트를 선택해야 한다.
순서대로 Dumny -> Stub -> Fake -> Spy -> Mock

참고

https://codinghack.tistory.com/92
https://tecoble.techcourse.co.kr/post/2020-09-19-what-is-test-double/

profile
자바 백엔드 개발자로 성장하자

0개의 댓글