inblog logo
|
LifeLog, DevLog
    SpringJava

    테스트에 관하여

    KYJTHEYJ's avatar
    KYJTHEYJ
    Jan 26, 2026
    테스트에 관하여
    Contents
    @Test주요 검증 메서드API 관련 테스트 + Spring Test 어노테이션적용 예시Spy

    @Test

    기능 개발을 하면 그 기능이 원체 단순하다면 작동 결과를 보고 판단할 수 있다

    허나 조금이라도 복잡한 케이스가 더해지면
    코드를 작동시켜서 확인하고 고치고.. 확인하고 고치고.. 확인하고 고치고..
    하는 과정을 하기엔 무리가 있다

    기능 작성 코드를 바탕으로 테스트 메서드를 작성해서 기대값과 실제값을 비교하여
    통과되는지, 실패되는지를 체킹하는 것이 올바른 테스트다

    이런 @Test 를 활용하여 기반으로 테스트 케이스를 먼저 작성하고
    기능 코드를 작성하는 방법론도 있는데 한번 쯤은 들어 봤을 법한 TDD 라고 한다

    주요 검증 메서드

    assertEquals/NotEquals

    기댓값과 같은지, 다른지 확인한다

    @Test
    void calcTest() {
    	int result = 2 + 3;
     	assertEquals(5, result);
    }

    assertTrue/False

    기댓값이 true, false 인지 확인한다

    @Test
    void valueBoolCheck() {
    	boolean isTrue = true;
    	assertTrue(isTrue);
    }

    assertNull/NotNull

    기댓값이 null인지 null이 아닌지 확인한다

    @Test
    void strCheck() {
    	String str = "testing";
    	assertNotNull(str);
    }

    assertThrows

    기대값 도출 중 예외 유형이 발생하는지 확인한다

    @Test
    void calcProcessExceptionCheck() {
    	Calculator calc = new Calaculator();
    	assertThrows(ArithmeticException.class, () -> {
    		calc.divide(10, 0); // 0으로 나누는 에러를 통해 예외 발생 확인
    	})
    }

    assertThat *

    기대값을 명시한 후 후속 메서드로 비교하는 방법
    다만 JUnit5 은 아니고 assertJ 에 포함된 검증 메서드이다

    assertThat, assertThatThrownBy(() → {}).isInstanceOf(exception.class)
    등 여러가지 형태가 있는데, 대부분의 테스트코드에 자주 쓰이므로 알아두어야 한다

    체이닝으로 지원하는 메서드는 여러가지가 있지만 대부분 명시된 메서드명만 보고도
    기능을 알 수 있다 (가독성이 좋다)

    @Test
    @DisplayName("constructor Test")
    void constructorTest() {
       String title = "testTitle";
       String content = "testContent";
       String weather = "testWeather";
    
        Todo todo = new Todo(title, content, weather, user);
    
        assertThat(todo.getTitle()).isEqualTo(title);
        assertThat(todo.getContents()).isEqualTo(content);
        assertThat(todo.getWeather()).isEqualTo(weather);
        assertThat(todo.getUser()).isEqualTo(user);
    }

    Verify *

    기대 코드가 호출되었는지를 확인하는 메서드

    Verify(Mock 객체, VerificationMode mode) 의 형태로 사용한다

    자주 사용되는 VerificationMode 로는

    • times(n) → n 번 호출 되었는지 검증

    • never → 한번도 호출 되지 않았는지 검증

    • timeout(long mills) → 해당 소요시간이 입력된 밀리세컨드 보다 높으면 탈락

    • only → 해당 메서드만 실행되었는지 검증

    등이 있는 것 같지만, 아마 자주 사용하게 되는건 times 가 아닐까 싶다

    @Test
        @DisplayName("deleteComment Test")
        void deleteComment() {
            commentAdminService.deleteComment(CommentFixture.id);
    
            verify(commentRepository, times(1)).deleteById(CommentFixture.id);
        }

    ReflectionTestUtils *

    테스트 객체에 값을 주입해야할 경우에 사용한다

    JwtUtil jwtUtil = new JwtUtil();
    
    ReflectionTestUtils.setField(jwtUtil, "secretKeyString", testSecretKey);

    Fixture

    검증의 테스트 객체, 검증에 필요한 정보를 세팅하는 것
    아래와 같이 정적 코드로 작성해서 실제 테스트 코드 내에서 불러 사용한다

    public class UserFixture {
        public static final Long adminId = 1L;
        public static final Long userId = 2L;
        public static final Long wrongId = 3L;
        public static final String userEmail = "user@test.com";
        public static final String userPassword = "1234Test";
        public static final String adminEmail = "admin@test.com";
        public static final String adminPassword = "Test1234";
        public static final UserRole userRole = UserRole.USER;
        public static final UserRole adminRole = UserRole.ADMIN;
    
        public static User registerUser() {
            User user = User.fromAuthUser(registerAuthUser());
            ReflectionTestUtils.setField(user, "password", userPassword);
            return user;
        }
    
        public static User registerAdmin() {
            User admin = User.fromAuthUser(registerAuthAdmin());
            ReflectionTestUtils.setField(admin, "password", adminPassword);
            return admin;
        }
    
        public static AuthUser registerAuthUser() {
            return new AuthUser(userId, userEmail, userRole);
        }
    
        public static AuthUser registerAuthAdmin() {
            return new AuthUser(adminId, adminEmail, adminRole);
        }
    }

    기본 사용 템플릿 (given-when-then)

    사전의 데이터로 (given)

    어떠한 동작을 하여 (when)

    어떠한 결과를 얻는지 (then) 를 비교면 된다

    @Test
    void 테스트명() {
    	// Given (준비)
            
    	// When (실행)
            
    	// Then (검증)
    	검증 메서드(기대값, 실제값);
    }

    API 관련 테스트 + Spring Test 어노테이션

    • @SprintBootTest → 실제 어플리케이션 구동시
      스프링 컨테이너에 로드되는 환경을 그대로 재현해주는 어노테이션

      • 다만 통합테스트에 사용에 옳고, 단위별 테스트 진행시는 하지 않는 게 맞다

    • @Test → 테스트 코드 명시 어노테이션

    Controller 관련

    • @WebMvcTest → 컨트롤러만 테스트하고 싶을 경우

    • @MockitoBean → 스프링 컨테이너에 가짜 빈을 올려줌

      • @MockBean, @Spy 는 현재 deprecated 된 상태

    • MockMvc → HTTP 요청 시뮬레이터

    Service 관련

    • @ExtendWith(MockitoExtension.class) → Mockito 프레임워크 활성화

    • @Mock → 가짜 객체, 메서드 호출해도 Null, false, 0 등 반환

    • @InjectMocks → 가짜 객체에 객체 주입 해주기

      • 쉽게 말해서 테스트 할 코드내 스프링 컨테이너가 주입한 의존성 → @Mock

      • 서비스 그 자체는 @InjectMocks

        @Service
        @RequiredArgsConstructor
        public class CommentAdminService { // <- InjectMocks
        
            private final CommentRepository commentRepository; // <- Mock
        
            @Transactional
            public void deleteComment(long commentId) {
                commentRepository.deleteById(commentId);
            }
        }

    Repository

    • @DataJpaTest → JPA 관련 Bean 만 로드, 인메모리 DB 사용

    적용 예시

    @ExtendWith(MockitoExtension.class)
    public class UserAdminServiceTest {
        @Mock
        private UserRepository userRepository;
    
        @InjectMocks
        private UserAdminService userAdminService;
    
        @Test
        @DisplayName("changeUserRole Test")
        void changeUserRoleTest() {
            User user = UserFixture.registerUser();
            UserRoleChangeRequest request = new UserRoleChangeRequest("ADMIN");
    
            given(userRepository.findById(UserFixture.userId)).willReturn(Optional.of(user));
    
            userAdminService.changeUserRole(UserFixture.userId, request);
    
            assertThat(user.getUserRole()).isEqualTo(UserRole.ADMIN);
        }
    
        @Test
        @DisplayName("changeUserRole Test - user Not Found Case")
        void changeUserRoleTest_Not_Found_User() {
            UserRoleChangeRequest request = new UserRoleChangeRequest("ADMIN");
    
            given(userRepository.findById(UserFixture.userId)).willReturn(Optional.empty());
    
            assertThatThrownBy(() -> userAdminService.changeUserRole(UserFixture.userId, request))
                    .isInstanceOf(InvalidRequestException.class)
                    .hasMessageContaining("User not found");
        }
    }
    

    Spy

    객체의 일부 기능을 테스트 하기 위해 사용하는 테스트 자료형

    실제 객체를 감싸서 만들며, 실제 메서드를 호출하는 역할을 한다

    Mock 은 내부의 특정 메서드 결과의 값을 가져와서 테스트하는 것
    또한 격리된 (의존성 자체를 제외) 하고 특정 메서드의 결과로 테스트 하는 것

    언제 사용해야 하는가?

    복잡한 로직 검증과 호출 여부보다는 상태의 변화를 측정할 때

    실제 코드가 작동하므로 주의하며 사용해야함

    Share article
    Contents
    @Test주요 검증 메서드API 관련 테스트 + Spring Test 어노테이션적용 예시Spy

    LifeLog, DevLog - https://github.com/KYJTHEYJ

    RSS·Powered by Inblog