Spring Framework/TDD / / 2024. 8. 7. 21:03

Mockito - 유닛 테스트를 위한 목 객체 활용법

Mockito는 자바에서 사용되는 인기있는 모의(Mocking) 프레임워크입니다. Mockito를 사용하면 테스트 중에 가짜(Mock) 객체를 생성하고, 그 객체의 동작을 설정하고, 동작을 검증할 수 있습니다. 아래에 Mockito를 사용하는 기본적인 방법을 설명하겠습니다.

  • Mockito 설정: Mockito를 사용하려면 먼저 Mockito 라이브러리를 프로젝트에 추가해야 합니다. 일반적으로 Maven, Gradle과 같은 의존성 관리 도구를 사용하여 다음과 같이 의존성을 추가할 수 있습니다:
<!-- Maven 의존성 설정 예시 -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.2.0</version>
    <scope>test</scope>
</dependency>
  • 목(Mock) 객체 생성: @Mock 어노테이션을 사용하거나 Mockito.mock() 메서드를 사용하여 목(Mock) 객체를 생성할 수 있습니다
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

// @Mock 어노테이션을 사용한 목(Mock) 객체 생성
@Mock
private UserDao userDao;

public void setUp() {
    MockitoAnnotations.openMocks(this);
}

// Mockito.mock() 메서드를 사용한 목(Mock) 객체 생성
UserDao userDao = Mockito.mock(UserDao.class);
  • 동작 설정: when().thenReturn()을 사용하여 목(Mock) 객체의 메서드 호출에 대한 동작을 설정할 수 있습니다.
import static org.mockito.Mockito.when;

// userDao.getUserById() 메서드 호출 시 동작 설정
when(userDao.getUserById(1)).thenReturn(new User("John"));
  • 동작 검증: verify() 메서드를 사용하여 목(Mock) 객체의 메서드 호출이 예상대로 이루어졌는지 검증할 수 있습니다.
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.never;

// userDao.updateUser() 메서드가 한 번 호출되었는지 검증
verify(userDao, times(1)).updateUser(any(User.class));

// userDao.deleteUser() 메서드가 호출되지 않았는지 검증
verify(userDao, never()).deleteUser(anyInt());
  • 추가적인 기능: Mockito는 다양한 기능을 제공하여 더 복잡한 테스트 시나리오를 처리할 수 있습니다. 예를 들어, 예외를 발생시키거나 순서에 따른 동작을 설정할 수 있습니다. Mockito의 공식 문서나 튜토리얼에서 더 많은 기능을 확인할 수 있습니다.

위의 단계들을 참고하여 Mockito를 사용하여 목(Mock) 객체를 생성하고, 동작을 설정하고, 동작을 검증할 수 있습니다. Mockito는 테스트 코드 작성을 더욱 쉽고 유연하게 만들어주는 강력한 도구입니다.


Mockito의 사용 방법에 대해 자세히 설명하겠습니다. 

1. Mock 객체 생성
Mock 객체는 실제 객체의 행동을 모방하는 객체로, 실제 코드의 외부 의존성을 대체합니다.
  기본 Mock 생성: Mockito.mock(Class) 메소드를 사용하여 mock 객체를 생성할 수 있습니다.  

List mockedList = Mockito.mock(List.class);


  어노테이션을 이용한 Mock 생성: @Mock 어노테이션을 사용하면 테스트 클래스 내에서 직접 mock 객체를 선언할 수 있습니다. 이를 위해 MockitoAnnotations.initMocks(this)를 테스트의 @Before 메소드에서 호출해야 합니다.  

@Mock
List mockedList;

@Before
public void init() {
    MockitoAnnotations.initMocks(this);
}



2. Mock 객체의 행동 정의
Mock 객체의 행동을 when-thenReturn 패턴을 사용하여 정의할 수 있습니다.
  when-thenReturn: 특정 조건에서 특정 값을 반환하도록 설정합니다. 

Mockito.when(mockedList.get(0)).thenReturn("first element");


  예외 던지기: 특정 조건에서 예외를 발생시킬 수 있습니다. 

Mockito.when(mockedList.get(anyInt())).thenThrow(new RuntimeException());


3. Mock 객체의 상호작용 검증 
테스트에서 mock 객체와의 상호작용을 검증할 수 있습니다. 
  verify 메소드: 특정 메소드가 특정 조건으로 호출되었는지 검증합니다.

mockedList.add("one");
Mockito.verify(mockedList).add("one");


  호출 횟수 검증: 메소드가 특정 횟수만큼 호출되었는지 검증할 수 있습니다.

Mockito.verify(mockedList, Mockito.times(1)).add("one");


4. Argument Matchers 
Mockito는 다양한 Argument Matchers를 제공하여 더 유연한 행동 정의와 검증을 가능하게 합니다. 
  any(), eq(), refEq(), anyInt() 등 다양한 matchers를 사용할 수 있습니다.

Mockito.when(mockedList.get(anyInt())).thenReturn("element");


5. Spy 객체 
Spy 객체는 실제 객체의 일부 행동을 모방하면서도 일부는 실제 객체의 행동을 유지하는 특별한 종류의 mock입니다. 
  Spy 객체 생성:

List list = new ArrayList();
List spyList = Mockito.spy(list);

 

  Spy 객체의 행동을 부분적으로 오버라이드 할 수 있습니다:

Mockito.doReturn("first element").when(spyList).get(0);


6. Mock 객체의 리셋 
테스트 중 mock 객체의 상태를 리셋할 필요가 있을 때 사용합니다. 
  Mockito.reset() 메소드를 사용하여 mock 객체를 초기 상태로 되돌립니다.

Mockito.reset(mockedList);

 


Mockito는 단위 테스트를 위한 강력하고 유연한 도구입니다. Mock 객체를 사용하여 실제 객체의 의존성을 제거하고, 테스트 대상의 독립성을 보장하여 보다 견고하고 유지보수가 용이한 테스트 코드를 작성할 수 있습니다. Mockito의 다양한 기능을 활용하여, 복잡한 비즈니스 로직을 가진 애플리케이션의 테스트를 효율적으로 진행할 수 있습니다.


우리는 UserService라는 사용자 서비스 클래스를 테스트하려고 합니다. UserService UserDao EmailService에 의존하고 있습니다. 우리는 UserService의 동작을 확인하고자 하는데, UserDao의 메서드 호출이 제대로 이루어지고 EmailService에 이메일이 제대로 전송되는지를 확인하려고 합니다.

 

1. 의존성 주입(Dependency Injection):
의존하는 객체들(UserDao, EmailService)을 목(Mock) 객체로 대체해야 합니다. 이를 위해 Mockito의 @Mock 어노테이션을 사용합니다.

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class UserServiceTest {
    @Mock
    private UserDao userDao;

    @Mock
    private EmailService emailService;

    @InjectMocks
    private UserService userService;

    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    // 테스트 메서드들...
}

위의 코드에서 @Mock 어노테이션은 UserDao EmailService의 목(Mock) 객체를 생성합니다. @InjectMocks 어노테이션은 UserService 객체를 생성하고, 그 안에 목(Mock) 객체들을 주입합니다. setUp() 메서드는 목(Mock) 객체들을 초기화하기 위해 MockitoAnnotations.openMocks(this)를 호출합니다.

 

2. 메서드 동작 설정 및 검증

테스트 시나리오에 따라 목(Mock) 객체들의 동작을 설정하고, 검증할 수 있습니다. 이를 위해 Mockito의 when() verify() 메서드를 사용합니다.

import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;

public class UserServiceTest {
    // ...

    public void testAddUser() {
        // UserDao 목(Mock) 객체의 동작 설정
        when(userDao.addUser(any(User.class))).thenReturn(true);

        // EmailService 목(Mock) 객체의 동작 설정
        doNothing().when(emailService).sendWelcomeEmail(any(User.class));

        // 테스트 대상 메서드 호출
        User user = new User("John", "john@example.com");
        boolean result = userService.addUser(user);

        // UserDao 메서드 호출 검증
        verify(userDao).addUser(user);

        // EmailService 메서드 호출 검증
        verify(emailService).sendWelcomeEmail(user);

        // 결과 검증
        assertTrue(result);
    }

    // ...
}

위의 코드에서 when() 메서드를 사용하여 userDao.addUser() 메서드가 호출될 때 true를 반환하도록 설정했습니다. doNothing().when(emailService).sendWelcomeEmail() emailService.sendWelcomeEmail() 메서드가 호출될 때 아무 동작도 하지 않도록 설정했습니다. 그리고 verify() 메서드를 사용하여 userDao.addUser() emailService.sendWelcomeEmail() 메서드가 각각 한 번씩 호출되었는지를 검증합니다.

 

3. 목(Mock) 객체의 추가적인 검증

실제 메서드 호출 시 특정 매개변수를 사용하는지, 메서드 호출 횟수 등을 검증할 수 있습니다.

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.never;

public class UserServiceTest {
    // ...

    public void testUpdateUser() {
        // UserDao 목(Mock) 객체의 동작 설정
        when(userDao.getUserById(1)).thenReturn(new User("John", "john@example.com"));

        // 테스트 대상 메서드 호출
        boolean result = userService.updateUserEmail(1, "newemail@example.com");

        // UserDao 메서드 호출 검증
        verify(userDao).getUserById(1);
        verify(userDao).updateUserEmail(1, "newemail@example.com");
        verify(userDao, never()).updateUserName(anyInt(), anyString());

        // EmailService 메서드 호출 검증
        verify(emailService, times(1)).sendEmail(anyString(), anyString(), anyString());

        // 결과 검증
        assertTrue(result);
    }

    // ...
}

위의 코드에서 userDao.getUserById() 메서드가 호출될 때 new User("John", "john@example.com")를 반환하도록 설정했습니다. userService.updateUserEmail() 메서드를 호출하고, userDao.getUserById() 및 userDao.updateUserEmail() 메서드의 호출을 검증합니다. userDao.updateUserName() 메서드는 호출되지 않아야 함을 never() 메서드로 검증했습니다. emailService.sendEmail() 메서드는 한 번 호출되어야 함을 times(1)로 검증합니다.

이제 위의 예제 코드를 참고하여 Mockito를 사용하여 목(Mock) 객체를 생성하고, 동작을 설정하고, 동작을 검증하는 방법에 대해 상세히 이해하실 수 있을 것입니다. Mockito는 테스트 환경에서 의존성을 가진 객체들을 쉽게 모의(Mock)할 수 있도록 도와주는 강력한 도구입니다.

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유