본문 바로가기
Backend/Spring

[TDD] Test Double

by 제이동 개발자 2025. 8. 3.
728x90

[TDD] Test Double

테스트를 위해 실제 구현을 대체하는 대역 객체를 통칭합니다.

 

종류 설명
Dummy 파라미터 전달용, 실제 동작 없음
Fake 간단한 실제 동작(예: 인메모리 DB)
Stub 미리 정의된 응답만 제공
Spy Stub 기능 + 호출 기록 기능
Mock 기대 행위 설정 및 호출 검증

 

 

1. 예시 코드

1-1. Dummy

// Dummy 구현: 실제로는 아무런 동작도 수행하지 않음
public class DummyPaymentGateway implements PaymentGateway {
    @Override
    public void pay(int amount) {
        // no-op
    }
}

class OrderServiceTest {
    @Test
    void placeOrder_withDummy_shouldReturnTrue() {
        PaymentGateway dummy = new DummyPaymentGateway();
        OrderService service = new OrderService(dummy);

        assertTrue(service.placeOrder(100)); // Gateway 동작 여부 상관없이 통과
    }
}

 

 

1-2. Fake

// In-memory Fake: Map 기반 간단 구현
public class InMemoryUserRepository implements UserRepository {
    private final Map<String, User> store = new HashMap<>();
    @Override
    public void save(User user) {
        store.put(user.getId(), user);
    }
    @Override
    public Optional<User> findById(String id) {
        return Optional.ofNullable(store.get(id));
    }
}

class UserServiceTest {
    @Test
    void findUser_withFakeRepo_shouldReturnUser() {
        UserRepository fakeRepo = new InMemoryUserRepository();
        User alice = new User("1", "Alice");
        fakeRepo.save(alice);

        Optional<User> found = fakeRepo.findById("1");
        assertTrue(found.isPresent());
        assertEquals("Alice", found.get().getName());
    }
}

 

1-3. Stub

// Stub: 고정 환율 리턴
public class StubExchangeRateService implements ExchangeRateService {
    @Override
    public double getRate(String from, String to) {
        return 1_200.0;
    }
}

// 테스트는 위의 상태 기반 검증 예시 참고

 

1-4. Spy

// Spy 구현: 호출 기록 저장
public class SpyEmailService implements EmailService {
    private final List<String> calls = new ArrayList<>();
    @Override
    public void send(String to, String body) {
        calls.add(to + ":" + body);
    }
    public List<String> getCalls() {
        return calls;
    }
}

class NotificationManagerTest {
    @Test
    void notifyUser_withSpy_shouldRecordCall() {
        SpyEmailService spy = new SpyEmailService();
        NotificationManager mgr = new NotificationManager(spy);

        mgr.notifyUser("user@example.com");
        assertEquals(1, spy.getCalls().size());
        assertEquals("user@example.com:Welcome!", spy.getCalls().get(0));
    }
}

 

1-5. Mock

import static org.mockito.Mockito.*;

class PaymentServiceTest {
    @Test
    void processPayment_withMock_shouldInvokeOnce() {
        // Mockito Mock
        PaymentGateway mockGateway = mock(PaymentGateway.class);
        OrderService service = new OrderService(mockGateway);

        service.placeOrder(50);

        // 기대 행위 검증
        verify(mockGateway, times(1)).pay(50);
    }
}
728x90