CS

Mock(Mocking)란 무엇인가?

10Biliion 2025. 3. 12. 10:32

 

 

소프트웨어 개발에서 테스트를 수행할 때, 실제 객체를 사용하면 여러 가지 문제가 발생할 수 있습니다. 예를 들어, 데이터베이스와 연동되거나 외부 API를 호출하는 코드가 포함된 경우, 테스트 실행 속도가 느려지고, 외부 서비스의 상태에 따라 테스트 결과가 달라질 수도 있습니다. 이러한 문제를 해결하기 위해 사용하는 기법이 Mocking(목킹) 입니다.

 

Mock의 개념

Mock은 실제 객체를 대신하는 가짜 객체(fake object)로, 특정한 동작을 미리 정의하여 테스트를 쉽게 수행할 수 있도록 도와줍니다. Mock 객체는 다음과 같은 상황에서 유용합니다.

  1. 외부 API 호출을 테스트해야 할 때: 실제 API를 호출하면 네트워크 지연이 발생하거나 응답이 예측할 수 없기 때문에, API 호출을 Mock으로 대체할 수 있습니다.
  2. 데이터베이스 연동 코드 테스트: 테스트할 때마다 DB에 접근하면 속도가 느려지고 데이터 정합성 문제가 발생할 수 있으므로, DB를 직접 사용하지 않고 Mock 객체로 대체합니다.
  3. 비즈니스 로직 단위 테스트: 복잡한 의존성을 가진 객체 대신 간단한 Mock 객체를 사용하여 특정 로직만 검증할 수 있습니다.

Mock과 Stub, Fake, Spy의 차이점

Mock과 유사한 개념으로 Stub, Fake, Spy 등이 있습니다.

개념 설명
Mock 특정한 동작을 미리 정의하고, 호출 여부를 검증할 수 있는 객체
Stub 정해진 응답을 반환하는 객체, 호출 여부는 확인하지 않음
Fake 간단한 로직을 포함한 대체 객체 (예: 인메모리 DB)
Spy 실제 객체를 감싸 호출을 기록하고 일부 동작을 변경할 수 있는 객체

Java에서 Mockito를 활용한 Mock 예제

Java에서는 Mockito 라이브러리를 사용하여 쉽게 Mock 객체를 만들 수 있습니다. 아래 예제는 간단한 유저 서비스(UserService)에서 사용자를 조회하는 기능을 Mocking하는 코드입니다.

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.*;

class UserServiceTest {
    @Test
    void testGetUserById() {
        // Mock 객체 생성
        UserRepository mockRepository = mock(UserRepository.class);
        UserService userService = new UserService(mockRepository);
        
        // Mock 동작 설정
        User mockUser = new User(1L, "John Doe");
        when(mockRepository.findById(1L)).thenReturn(mockUser);
        
        // 테스트 수행
        User result = userService.getUserById(1L);
        
        // 검증
        assertNotNull(result);
        assertEquals("John Doe", result.getName());
        
        // 특정 메소드가 호출되었는지 검증
        verify(mockRepository, times(1)).findById(1L);
    }
}

코드 설명

  1. mock(UserRepository.class): UserRepository의 Mock 객체를 생성합니다.
  2. when(mockRepository.findById(1L)).thenReturn(mockUser): 특정한 입력(1L)에 대해 원하는 결과(mockUser)를 반환하도록 설정합니다.
  3. verify(mockRepository, times(1)).findById(1L): 해당 메서드가 한 번 호출되었는지 검증합니다.

 

패스워드 암호화와 Mock 적용

회원 가입이나 로그인 기능을 테스트할 때, 패스워드는 반드시 암호화되어야 합니다. 실제 암호화 로직을 실행하면 테스트 속도가 느려질 수 있으므로, Mock을 활용하여 암호화 과정을 대체할 수 있습니다.

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class AuthServiceTest {
    @Test
    void testEncryptPassword() {
        // Mock 객체 생성
        PasswordEncoder mockEncoder = mock(PasswordEncoder.class);
        AuthService authService = new AuthService(mockEncoder);
        
        // Mock 동작 설정
        when(mockEncoder.encode("password123")).thenReturn("hashedPassword");
        
        // 테스트 수행
        String encryptedPassword = authService.encryptPassword("password123");
        
        // 검증
        assertEquals("hashedPassword", encryptedPassword);
        verify(mockEncoder, times(1)).encode("password123");
    }
}

코드 설명

  1. PasswordEncoder를 Mock 객체로 생성하여 실제 암호화 로직을 대체합니다.
  2. when(mockEncoder.encode("password123")).thenReturn("hashedPassword"): 특정 입력값에 대해 암호화된 값을 반환하도록 설정합니다.
  3. verify(mockEncoder, times(1)).encode("password123"): 암호화 메서드가 한 번 호출되었는지 검증합니다.

 

 

  • 과도한 Mock 사용을 피해야 합니다. Mock 객체를 남용하면 실제 코드의 동작을 제대로 테스트하지 못할 수도 있습니다.
  • 실제 객체와의 차이를 이해해야 합니다. Mock 객체는 단순히 설정된 동작만 수행하므로, 실제 객체와의 차이점을 고려해야 합니다.
  • 테스트가 지나치게 Mock에 의존하지 않도록 해야 합니다. Mock을 많이 사용하면 코드 변경 시 테스트가 쉽게 깨질 수 있습니다.

'CS' 카테고리의 다른 글

API Gateway란?  (0) 2025.03.12
멀티스레드(Multi-threading)란?  (1) 2025.03.11
해시 테이블(Hash Table) 이란?  (0) 2025.01.23
CPU 스케줄링 이란?  (1) 2025.01.21
프로세스(Process)와 스레드(Thread)  (1) 2025.01.21