SOFTMOA TECHNOLOGY

Java 테스트 - JUnit 5와 Mockito

소프트모아가 정리한 기술 기록입니다.

Contents
작성일 2025. 11. 30.

JUnit 5와 Mockito 테스트


JUnit 5는 Java 테스트 프레임워크이고, Mockito는 모킹 라이브러리입니다. 함께 사용하면 외부 의존성을 격리하고 단위 테스트를 효과적으로 작성할 수 있습니다.



언제 사용하나요?



  • Service 클래스 단위 테스트

  • Repository 의존성 모킹

  • 외부 API 호출 모킹

  • 예외 상황 테스트



의존성 추가


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- 포함됨: JUnit 5, Mockito, AssertJ -->


기본 테스트 구조


import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {

private Calculator calculator;

@BeforeEach
void setUp() {
calculator = new Calculator();
}

@Test
@DisplayName("두 수의 합을 계산한다")
void add_shouldReturnSum() {
// given
int a = 10, b = 20;

// when
int result = calculator.add(a, b);

// then
assertEquals(30, result);
}

@Test
void divide_shouldThrowException_whenDivideByZero() {
assertThrows(ArithmeticException.class, () -> {
calculator.divide(10, 0);
});
}

@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void isPositive_shouldReturnTrue(int number) {
assertTrue(calculator.isPositive(number));
}
}


Mockito 기본 사용


import org.mockito.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

@Mock
private UserRepository userRepository;

@InjectMocks
private UserService userService;

@Test
void findById_shouldReturnUser() {
// given
User mockUser = new User(1L, "홍길동", "[email protected]");
when(userRepository.findById(1L))
.thenReturn(Optional.of(mockUser));

// when
User result = userService.findById(1L);

// then
assertNotNull(result);
assertEquals("홍길동", result.getName());
verify(userRepository, times(1)).findById(1L);
}

@Test
void findById_shouldThrowException_whenNotFound() {
// given
when(userRepository.findById(anyLong()))
.thenReturn(Optional.empty());

// then
assertThrows(UserNotFoundException.class, () -> {
userService.findById(999L);
});
}
}


Stubbing 패턴


// 단순 반환
when(mock.method()).thenReturn(value);

// 예외 발생
when(mock.method()).thenThrow(new RuntimeException());

// 연속 호출 다른 결과
when(mock.method())
.thenReturn(first)
.thenReturn(second)
.thenThrow(new Exception());

// 파라미터 매칭
when(mock.method(eq("specific")))
.thenReturn(value);
when(mock.method(anyString()))
.thenReturn(defaultValue);

// 콜백으로 동적 반환
when(mock.method(anyLong())).thenAnswer(invocation -> {
Long id = invocation.getArgument(0);
return new User(id, "User" + id);
});


검증 (Verification)


// 호출 횟수 검증
verify(mock, times(1)).method();
verify(mock, never()).method();
verify(mock, atLeast(2)).method();
verify(mock, atMost(3)).method();

// 호출 순서 검증
InOrder inOrder = inOrder(mock1, mock2);
inOrder.verify(mock1).firstMethod();
inOrder.verify(mock2).secondMethod();

// 파라미터 캡처
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
User captured = captor.getValue();
assertEquals("홍길동", captured.getName());


Spring Boot 통합 테스트


@SpringBootTest
@Transactional
class UserServiceIntegrationTest {

@Autowired
private UserService userService;

@Autowired
private UserRepository userRepository;

@Test
void createUser_shouldSaveToDatabase() {
// given
UserDto dto = new UserDto("홍길동", "[email protected]");

// when
User saved = userService.createUser(dto);

// then
User found = userRepository.findById(saved.getId()).orElseThrow();
assertEquals("홍길동", found.getName());
}
}

// MockMvc로 Controller 테스트
@WebMvcTest(UserController.class)
class UserControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private UserService userService;

@Test
void getUser_shouldReturnUser() throws Exception {
when(userService.findById(1L))
.thenReturn(new User(1L, "홍길동"));

mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("홍길동"));
}
}


@Spy 사용


// 실제 객체 + 일부 메서드만 모킹
@Spy
private UserService userService = new UserService();

@Test
void test() {
// 특정 메서드만 stubbing
doReturn(mockUser).when(userService).findById(1L);

// 나머지는 실제 로직 실행
userService.processUser(1L);
}


테스트 생명주기


@BeforeAll      // 클래스 시작 전 1회
@BeforeEach // 각 테스트 전
@AfterEach // 각 테스트 후
@AfterAll // 클래스 종료 후 1회

@Disabled // 테스트 비활성화
@Timeout(5) // 시간 제한
Share this

Java

Have a questions?

견적 및 기술문의

mobile : 010-7931-4813

Contact Form