| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
- 단위테스트
- 자바
- javascript
- ArrayList
- 자바스크립트
- Eclipse
- SpringBoot
- IntelliJ
- java테스트
- 스프링부트
- string
- input
- vscode
- 문자열
- js
- Visual Studio Code
- list
- HashMap
- html
- 정규식
- 인텔리제이
- junit
- junit5
- CSS
- 배열
- 테스트자동화
- Array
- 자바문법
- Java
- math
- Today
- Total
어제 오늘 내일
[Spring Boot] "테스트 없는 배포는 도박이다!" JUnit5 & Mockito 단위 테스트 입문 본문
[Spring Boot] "테스트 없는 배포는 도박이다!" JUnit5 & Mockito 단위 테스트 입문
hi.anna 2026. 3. 14. 07:51
"바빠서 테스트 짤 시간이 없어요."라는 핑계, 많이 들어보셨죠? 하지만 테스트 코드가 없는 코드는 '레거시(Legacy)'일 뿐입니다. 테스트가 없으면 리팩토링도, 기능 추가도 무서워서 못 하게 되니까요.
오늘은 스프링 부트 테스트의 양대 산맥, JUnit5(실행기)와 Mockito(가짜 객체)를 사용해 서비스 계층(Service Layer)을 테스트하는 방법을 알아보겠습니다.
1. 단위 테스트(Unit Test)란?
"딱 하나(Unit)만 격리해서 테스트한다"는 뜻입니다.
우리가 MemberService를 테스트하고 싶은데, 이 서비스가 MemberRepository를 통해 진짜 DB에 접근한다면?
- 테스트가 느려집니다. (DB 통신)
- 데이터가 꼬입니다. (테스트할 때마다 DB 초기화 필요)
- 결론: 서비스 로직만 검증하고 싶은데, DB 때문에 방해를 받습니다.
그래서 우리는 진짜 DB 대신 가짜(Mock) 객체를 사용하여, 오직 서비스 로직에만 집중합니다. 이것이 바로 Mocking입니다.
2. 필수 친구들 소개
- JUnit5: 자바용 단위 테스트 프레임워크입니다. (
@Test등을 제공) - Mockito: 가짜 객체(Mock)를 쉽고 간편하게 만들어주는 라이브러리입니다.
- AssertJ: 테스트 결과를 검증(
assertThat)하는 라이브러리입니다. (JUnit 기본assertEquals보다 훨씬 가독성이 좋습니다!)
3. 실전! 회원가입 테스트 코드 작성하기
자, MemberService의 join() 메서드를 테스트해 봅시다.
① 테스트 클래스 세팅
@ExtendWith(MockitoExtension.class) // 1. Mockito를 쓰겠다고 선언
class MemberServiceTest {
@Mock // 2. 가짜 리포지토리 생성 (껍데기만 있음)
private MemberRepository memberRepository;
@InjectMocks // 3. 가짜 리포지토리를 이 서비스에 주입!
private MemberService memberService;
// ... 테스트 메서드 작성
}
② BDD 스타일 (Given - When - Then)
테스트 코드는 무조건 이 3단계를 따르는 것이 국룰(Rule)입니다.
- Given (준비): 이런 상황이 주어졌을 때
- When (실행): 이 코드를 실행하면
- Then (검증): 이런 결과가 나와야 한다
@Test
@DisplayName("회원가입 성공 테스트")
void join() {
// given (준비)
Member member = new Member("userA", "password123");
// 가짜 리포지토리에게 "save()가 호출되면, 무조건 member를 리턴해!"라고 교육(Stubbing)시킴
// (실제 DB에 저장 안 함)
given(memberRepository.save(any())).willReturn(member);
// when (실행)
Long savedId = memberService.join(member);
// then (검증)
// 리턴된 ID가 null이 아니어야 한다.
assertThat(savedId).isNotNull();
// save() 메서드가 딱 1번 호출되었는지 검사 (행위 검증)
verify(memberRepository, times(1)).save(member);
}
4. 실패 케이스도 테스트해야죠! (예외 발생)
성공하는 테스트보다 더 중요한 게 실패하는 테스트입니다.
"중복 회원이 가입하면 예외가 터져야 한다"는 시나리오를 짜볼까요?
@Test
@DisplayName("중복 회원 예외 발생")
void join_duplicate() {
// given
Member member1 = new Member("userA", "1234");
Member member2 = new Member("userA", "5678"); // 이름 같음!
// "이름으로 찾았을 때 이미 회원이 있다고 해!" (가짜 행동 정의)
given(memberRepository.findByName("userA"))
.willReturn(Optional.of(member1));
// when & then (실행과 검증을 동시에)
// memberService.join(member2)를 실행했을 때, IllegalStateException이 터져야 한다!
assertThatThrownBy(() -> memberService.join(member2))
.isInstanceOf(IllegalStateException.class)
.hasMessage("이미 존재하는 회원입니다.");
}
5. Mockito 핵심 요약 (given vs when)
Mockito를 쓸 때 가장 헷갈리는 부분입니다.
- Stubbing (가짜 행동 정의):
given(mock.method()).willReturn(value): BDD 스타일 (추천!)when(mock.method()).thenReturn(value): 기본 스타일
- Verification (행위 검증):
verify(mock).method(): 이 메서드가 호출되었니?
마치며
오늘의 결론입니다.
- 단위 테스트는 외부 의존성(DB, Network)을 끊고 내 로직만 검증하는 것이다.
- Mockito를 사용해 가짜(Mock) 객체를 주입(
@InjectMocks)받아 테스트한다. - Given-When-Then 패턴으로 작성하여 가독성을 높인다.
- 실패 케이스(예외)를 반드시 꼼꼼하게 테스트한다.
이렇게 단위 테스트를 작성해 두면, 나중에 코드를 대대적으로 수정해도 "테스트 돌려보니 초록불(Pass)이네? 안심하고 배포하자!" 할 수 있는 자신감이 생깁니다.
다음 포스팅에서는 "그래도 진짜 DB랑 잘 붙는지 확인해야지!" 통합 테스트(@SpringBootTest)와 H2 데이터베이스 활용법에 대해 알아보겠습니다.
도움이 되셨다면 좋아요와 댓글 부탁드립니다! 😊
