Spies
Spies Spies는 Test Double 기법 중 하나로, 실제 객체의 메서드 호출을 추적하고 기록하는 데 사용된다. 목적 메서드 호출 여부, 횟수, 전달된 인자 등을 검증한다. 실제 구현을 변경하지 않고 메서드의 동작을 관찰한다. 코드의 상호작용을 분석하고 테스트한다. 장점 비침투적: 실제 객체의 동작을 변경하지 않고 관찰할 수 있다. 유연성: 다양한 정보를 수집하고 검증할 수 있다. 상세한 검증: 메서드 호출의 세부 사항을 정확히 확인할 수 있다. 단점 복잡성: 과도한 사용 시 테스트 코드가 복잡해질 수 있다. 오버스펙: 구현 세부사항에 너무 의존적인 테스트를 작성할 위험이 있다. 성능: 많은 spy를 사용할 경우 테스트 실행 속도가 느려질 수 있다. 예시 예시 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 from typing import Dict, Optional from datetime import datetime # 실제 데이터베이스 리포지토리 class UserRepository: def __init__(self, database_connection): self.db = database_connection def save(self, user_id: str, user_data: Dict): # 실제로는 데이터베이스에 SQL 쿼리를 실행할 것입니다 self.db.execute( "INSERT INTO users (id, data, created_at) VALUES (?, ?, ?)", [user_id, user_data, datetime.now()] ) def find_by_id(self, user_id: str) -> Optional[Dict]: # 실제로는 데이터베이스에서 조회할 것입니다 result = self.db.execute( "SELECT * FROM users WHERE id = ?", [user_id] ) return result.fetchone() # Fake 리포지토리 class FakeUserRepository: def __init__(self): # 데이터베이스 대신 딕셔너리를 사용 self.users: Dict[str, Dict] = {} def save(self, user_id: str, user_data: Dict): # 메모리에 직접 저장 self.users[user_id] = { 'data': user_data, 'created_at': datetime.now() } def find_by_id(self, user_id: str) -> Optional[Dict]: # 메모리에서 직접 조회 return self.users.get(user_id) # 사용자 서비스 class UserService: def __init__(self, user_repository): self.repository = user_repository def create_user(self, user_id: str, name: str, email: str): user_data = {'name': name, 'email': email} self.repository.save(user_id, user_data) def get_user(self, user_id: str): return self.repository.find_by_id(user_id) # 테스트 코드 def test_user_service(): # Fake 리포지토리 사용 fake_repository = FakeUserRepository() user_service = UserService(fake_repository) # 사용자 생성 테스트 user_service.create_user('user1', 'John Doe', 'john@example.com') # 사용자 조회 테스트 user = user_service.get_user('user1') assert user['data']['name'] == 'John Doe' assert user['data']['email'] == 'john@example.com' JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 // 실제 외부 API 서비스 class WeatherService { async getTemperature(city) { // 실제로는 외부 API를 호출할 것입니다 const response = await fetch( `https://api.weather.com/${city}/temperature` ); return response.json(); } } // Fake 날씨 서비스 class FakeWeatherService { constructor() { // 미리 정의된 도시별 온도 데이터 this.temperatureData = { 'Seoul': { temperature: 25 }, 'New York': { temperature: 20 }, 'London': { temperature: 15 } }; } async getTemperature(city) { // 실제 API 호출 대신 저장된 데이터 반환 return Promise.resolve(this.temperatureData[city] || { temperature: 0 }); } } // 날씨 알림 서비스 class WeatherAlertService { constructor(weatherService) { this.weatherService = weatherService; } async shouldSendAlert(city) { const data = await this.weatherService.getTemperature(city); return data.temperature > 30; } } // 테스트 코드 describe('WeatherAlertService', () => { it('should not send alert for normal temperature', async () => { // Fake 날씨 서비스 사용 const fakeWeatherService = new FakeWeatherService(); const alertService = new WeatherAlertService(fakeWeatherService); const shouldAlert = await alertService.shouldSendAlert('Seoul'); expect(shouldAlert).toBe(false); }); }); 참고 및 출처