TIL - 26.01.26

이준연·2026년 1월 26일

학습 키워드


  • JUnit 기초
  • 단위 테스트(Unit Test)

JUnit


JUnit이란?

Java 진영의 대표적인 단위 테스트 프레임워크

  • 테스트 코드를 작성 및 자동으로 실행, 검증할 수 있도록 돕는 도구
  • Spring Boot 기본 포함

JUnit 흐름

  • @BeforeEach → @Test → @AfterEach
    • 각 테스트 메서드는 독립적으로 실행
    • 하나의 테스트 실패가 다른 테스트에 영향을 주지 않음
import org.junit.jupiter.api.*;

class CalculatorTest {

    Calculator calculator;

    @BeforeEach
    void setUp() {
        calculator = new Calculator();
        System.out.println("BeforeEach 실행");
    }

    @AfterEach
    void tearDown() {
        System.out.println("AfterEach 실행");
    }

    @Test
    @DisplayName("더하기 기능 테스트")
    void add() {
        int result = calculator.add(2, 3);
        Assertions.assertEquals(5, result);
    }

    @Test
    @DisplayName("빼기 기능 테스트")
    void subtract() {
        int result = calculator.subtract(5, 2);
        Assertions.assertEquals(3, result);
    }
}

주요 어노테이션

  • @Test: 테스트 메서드 표시
  • @BeforeEach: 각 테스트 실행 전 초기화
  • @AfterEach: 각 테스트 실행후 정리
  • @BeforeAll: 전체 테스트 시작 전 1회 실행(static 필요)
  • @AfterAll: 전체 테스트 끝난 후 1회 실행(static 필요)
  • @DisplayName: 테스트 이름 커스텀
  • @Disabled: 특정 테스트 임시 비활성화

단언문

  • assertEquals(expected, actual): 값이 같은지 확인
  • assertTrue(condition): 조건이 참인지 확인
  • assertFalse(condition): 조건이 거짓인지 확인
  • assertThrows(Exception.class, () -> 실행코드): 특정 예외 발생 검증
  • assertAll(): 여러 검증을 묶어서 실행
  • assertNotNull(object): 객체가 null이 아닌지 확인

테스트 예시

  • 메서드명_상황_기대결과 규칙을 사용하여 메서드명 작성
  • Given-When-Then 패턴(사전 조건, 실행 동작, 결과 검증)
@Test
@DisplayName("Given-When-Then 패턴 예시")
void add_2더하기3_결과는5() {
    // Given
    Calculator calc = new Calculator();

    // When
    int result = calc.add(2, 3);

    // Then
    assertEquals(5, result);
}

@Test
@DisplayName("0으로 나누면 예외 발생해야 함")
void divide_0으로나누기_예외() {
    // Given
    Calculator calc = new Calculator();

    // When & Then
    assertThrows(ArithmeticException.class, () -> calc.divide(10, 0));
}

Unit Test


Unit Test란?

한 단위가 의도대로 동작하는지를 독립적으로 검증하는 것

Mock 객체의 개념

  • 테스트를 위해 가짜로 만든 객체
  • 단위 테스트는 외부 의존성을 제거하기 위해 Mock 객체를 사용

Mock 필요 이유

  • 속도향상: DB, 네트워크 없이 빠르게 테스트 가능
  • 독립성 확보: 특정 클래스만 검증
  • 예측 가능성: Mock의 동작을 직접 지정할 수 있음
  • 안정성 향상: 외부 장애나 환경 문제로 테스트 실패 방지

주요 어노테이션

  • @Mock: 가짜 객체 생성
  • @InjectMocks: Mock을 주입받는 테스트 대상 클래스
  • @ExtendWith(MockitoExtension.class): Mockito 환경 통합
  • when(...).thenReturn(...): Mock 동작 정의
  • verify(..., times(n)): 메서드 호출 횟수 검증

Mock 테스트

사용 조건

  • 내부 로직이 Repository를 의존
  • DB와 직접 연결

대상 코드

    public PostDto creatPost(String username, String content) {

        User user = userRepository.findUserByUsername(username).orElseThrow(
            ()-> new IllegalArgumentException("등록된 사용자가 없습니다.")
        );

        Post post = postRepository.save(new Post(content, user));

        return PostDto.from(post);

    }

테스트 코드

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class PostServiceTest {

    @Mock
    private PostRepository postRepository;
    @Mock
    private UserRepository userRepository;
    @InjectMocks
    private PostService postService;

    private User testUser;
    private Post testPost;

    @BeforeEach
    void setup() {
        testUser = new User("ravi", "1234", "ravi@example.com", 25, UserRoleEnum.ADMIN);
        testPost = new Post("테스트 게시글입니다.", testUser);
        ReflectionTestUtils.setField(testPost, "id", 1L);
    }

    @Test
    @DisplayName("게시글 생성 성공")
    void createPost_성공() {
        // Given
        when(userRepository.findByUsername("ravi")).thenReturn(Optional.of(testUser));
        when(postRepository.save(any(Post.class))).thenReturn(testPost);

        // When
        PostDto result = postService.createPost("ravi", "테스트 게시글입니다.");

        // Then
        assertThat(result.getContent()).isEqualTo("테스트 게시글입니다.");
        assertThat(result.getUsername()).isEqualTo("ravi");
        verify(postRepository, times(1)).save(any(Post.class));
    }
}

profile
반갑습니다!

0개의 댓글