12.05 TIL - 단위 테스트

이서준·2025년 12월 5일

TIL

목록 보기
15/24

단위 테스트

  • 프로그램의 가장 작은 실행 단위가 의도대로 동작하는지 독립적으로 검증하는 테스트

외부 요인과 관계 없이, 코드 자체가 정확히 동작하는지 테스트

특징

1. 독립성 (Isolation)

  • 단위 테스트는 실제 DB, 외부 API, 파일 시스템 등에 의존하면 안됨
  • 테스트는 실패 원인이 코드 때문인지, DB 때문인지 구분이 안되기 때문

2. 빠른 속도

  • 단위 테스트는 빨리 끝나야함 (ms)
  • DB 쿼리, 네트워크 호출이 들어가면 속도가 급격히 느려짐

3. 반복 가능성

  • 같은 테스트는 언제 돌려도 같은 결과가 나와야 함
  • 외부 환경 영향을 받으면 단위 테스트가 아님

4. 예측 가능성

  • 테스트 결과가 랜덤이면 안됨
  • 의존 객체를 Mock으로 제어하기 때문에 결과를 사용자가 통제 함

Mock 객체

  • 실제 객체처럼 동작하지만, 테스트를 위해 가짜로 만든 객체
  • 단위 테스트가 외부 의존성에 영향을 받지 않도록 하려면 반드시 Mock객체 사용

Mock 필요성

이유설명
속도 향상DB / 네트워크 없이 빠르게 테스트
독립성 확보특정 클래스만 검증
예측 가능성외부 객체의 응답을 강제로 지정 가능
안정성네트워크 장애, DB 오류로 인한 테스트 실패 방지

Mockito 주요 어노테이션

어노테이션설명
@Mock가짜 객체 생성
@InjectMocksMock 객체를 주입받아 테스트 대상 객체 생성
@ExtendWith(MockitoExtension.class)Mockito 테스트 환경 사용
when(...).thenReturn(...)Mock의 동작 정의
verify(..., times(n))메서드 호출 횟수 검증
any()파라미터를 특정 값으로 제한하지 않고 수용
assertThrows()예외 발생 검증

Controller 단위 테스트

  1. Spring MVC 동작
    1. URL 매핑
    2. JSON 직렬화
    3. HTTP 요청/응답
  2. Service 호출

사용 도구

  • MockMvc
  • @WebMvcTest

예시

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    @DisplayName("비밀번호 변경 - AuthUser 없을 때 - 실패 케이스")
    void ChangePassword_Not_AuthUser_fail() throws Exception {
        //given

        //when & then
        mockMvc.perform(put("/users")
                        .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.detail").value("유효하지 않은 UserRole"));

    }

}

핵심

  • Controller 테스트는 Spring MVC 동작 + JSON 요청/응답 구조 + validation 만 테스트 할 것

Util 클래스 단위 테스트 - Mock 필요 없음

  • 외부 의존성이 없기 때문에 Mock이 필요 없음
  • 순수한 로직만 테스트
  • 속도 매우 빠름

예시

class JwtUtilTest {

	private JwtUtil jwtUtil;

	@BeforeEach
	void setUp() {
		jwtUtil = new JwtUtil();

		String secretKey = "QWEASDzxcASDzxcrtyfghvbnuiojklmqweasdzxcasdHFGHGFHGF";
		ReflectionTestUtils.setField(jwtUtil, "secretKey", secretKey);
		jwtUtil.init();
}

	@Test
	@DisplayName("JWT 토큰 생성 TEST - 성공 케이스")
	void validateToken_success() {

		//given
		Long userId = 1L;
		String email = "lee@seo.jun";
		UserRole role = UserRole.ADMIN;

		//when
		String token = jwtUtil.createToken(userId, email, role);

		//then
		assertThat(token).startsWith("Bearer ");
	}
}

Service 단위 테스트

  • Mock 객체 필수

예시

@ExtendWith(MockitoExtension.class)
class ManagerServiceTest {

	@Mock
	private TodoRepository todoRepository;
	@InjectMocks
	private ManagerService managerService;

    @Test
    public void manager_목록_조회_시_Todo가_없다면_ManagerException_에러를_던진다() {
        // given
        long todoId = 1L;
        given(todoRepository.findById(todoId)).willReturn(Optional.empty());

        // when & then
        ManagerException exception = assertThrows(ManagerException.class, () -> managerService.getManagers(todoId));
        assertEquals("Todo not found", exception.getMessage());
    }
}

핵심

  • Repository 외부 인프라에 의존해서 Mock객체 필수
  • given(), when()을 적절히 정의
  • 반환값 검증
profile
Allons-y

0개의 댓글