(말레이시아의 유명한(?) 디버그 신사 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ)
근데 막상 디버그가 되어도 내 원하는 결과값이 안나온다면 말짱도루묵....ㅠㅠㅠ
그래서 원하는 결과값이 나오는지 확인하기 위한 테스트 코드가 존재한다.
맨토님께서 "테스트 코드도 작성하는것이 중요하다" 라고 하셨다.
팀원들도 그렇고 아무도 제대로 Spring Boot에서 테스트 코드를 작성해본 적이 없다 ㅠㅠ
쓰읍... 근데 강의에서 그냥 맛보기로 몇번만 써봤지 한번도 제대로 써 본적이 없기에,
한번 알아보자
정보처리기사 공부하다가 외운 테스트 방식 단.통.시.인
테스트 방식이 총 4가지가 있다.
하나의 모듈을 기준, 독립적으로 진행되는 가장 작은 단위의 테스트
(각 계층에서의 하나의 기능 또는 메소드)
모듈을 통합하는 과정에서 모듈 간 호환성을 확인하기 위한 테스트
다른 객체들과 데이터를 주고받으며 복잡한 기능이 수행 될 때, 연관된 객체들과 올바르게 동작하는지, 전체적인 연관 기능과 흐름 등을 파악하는 테스트
개발 프로젝트 차원에서 정의된 전체 시스템 또는 제품의 동작에 대해 테스트하는 단계
계약상의 요구사항이 만족되었는지 확인하기 위한 테스트 단계
(알파 / 베타 테스트가 여기에 포함)
일반적으로 말하는 테스트 코드는 거의 단위 테스트를 진행한다고 보면 된다.
왜냐하면 통합 테스트는 상호작용을 테스트 하기 때문에 모든 컴포넌트들이 구동된 상태에서 테스트를 해야하기에.. (Redis, DB 등등)
그래서 단위 테스트는 테스트하고자 하는 부분만 독립적으로 테스트를 하기 때문에 문제 여부를 빨리빨리 확인 가능하다는 장점이 있다.
즉, 우리는 지금부터 Spring Boot의 "단위 테스트" 방식을 알아볼 예정이다.
@Test
@DisplayName("Test")
void test() {
// Given
String name = "아아"
int prive = 1000;
int expectedPrice = 900;
// cafeRepository.findByName(name)을 사용하면 저 값을 Return 한다고 가정
when(cafeRepository.findByName(name))
.thenReturn({"스타벅스", "위치좌표" ... 등등})
// When
int actualPrice = cafeService.getDiscountedPrice(name);
// Then
assertEquals(price, actualPrice);
}
Given-When-Then 은 곧 [준비 - 실행 - 검증] 이다.
아주 심플하지만, 테스트 코드를 작성 시에 준비/실행/검증 의 세 부분으로 나누기만 하면 된다.
Mockito: 개발자가 동작을 직접적으로 제어할 수 있는 실제 객체의 동작을 모방하는 모의 객체
Mockito는
객체들 간의 의존성 → Mockito
를 이용하여 단절 → 단위 테스트를 쉽게 작성
이런 흐름으로 진행된다.
간단하게 말해서 단위 테스트는 정말 코드 로직을 보고 잘 나오는지 판단하는 테스트 방식이다.
예를 들면 DB에서 Cafe 데이터를 가져온다고 가정하자.
우린 DB에서 cafeRepository.findName("카페이름")
으로 값을 가져와야 정상이지만, 이것을 Mockito 객체로 정의해 가져왔다고 가정하고 테스트를 진행하도록 돕는 모의 객체다.
첫번째 방식은 JUnit5만을 이용해서 하는 테스트 방식이다.
실제로 DB연결을 해서 값을 비교하고 맞으면 Pass되는 방식
두번째가 JUnit 5 + Mockito를 같이 이용한 방식이다.
DB에서 값을 가져오는 것이 아닌, Mock Object라는 모의객체로 DB를 모방하는 객체로 사용이 되는 것이다.
모의 객체 생성(Mock) → 메서드 호출 예상 동작 설정(Stub) → 메서드 호출 검증(Verify)
A. 모의 객체 생성: Mock
→ 예) '모의(가짜) 리스트'를 생성
→ 실제 리스트와 동일한 방식으로 동작을 하지만, 이 동작을 사전에 정의 할 수 있다.
B. 메서드 호출 예상 동작 설정: Stub
→ 메서드 호출에 대한 ‘예상 동작’을 정의
→ 예) MockList에서 size() 메서드가 호출이 되었을 경우, 5를 반환하도록 지정
→ 예) mockList.size()를 호출 하였을때 5의 값이 추출
C. 메서드 호출 검증: Verify
→ 특정 메서드가 호출되고 예상된 인자와 함께 호출되었는지를 검증하는 메서드
→ 예) mockList 내에 .size() 메서드가 호출이 되었는지 검증을 수행
D. 예시 코드
@Test
@DisplayName("Mockito를 사용한 테스트 방식")
void testSimple() {
// 1. 모의 객체 생성: Mock
List<String> mockList = Mockito.mock(List.class);
// 2. 메서드 호출 예상 동작 설정: Stub
Mockito.when(mockList.size()).thenReturn(5);
System.out.println("동작 값을 확인 합니다 :: " + mockList.size()); // 동작 값을 확인
// 3. 메서드 호출 검증: Verify
Mockito.verify(mockList).size();
}
메소드명 | 설명 |
---|---|
thenReturn | Stub 메소드 호출 후, 어떤 객체를 반환 |
thenThrow | Stub 메소드 호출 후, 어떤 예외를 반환 |
thenAnswer | Stub 메소드 호출 후, 어떤 작업을 할지 (공식 문서에서는 이 메소드 사용을 비추천) |
thenCallRealMethod | 실제 메소드 호출 |
@Mock
UserService userService;
@Test
void test_thenReturn() {
// Given
User mockUser = mock(User.class); // Mock 객체 생성
given(mockUser.getEmail()).willReturn("test123@naver.com"); // 동작 정의
// When
String email = mockUser.getEmail(); // 동작 실행
// Then
assertEquals("test123@naver.com", email); // 검증
}
메소드명 | 설명 |
---|---|
doReturn | 스터빙 메소드 호출 후 어떤 행동을 할 건지 정의 |
doThrow | 스터빙 메소드 호출 후 어떤 Exception을 반환할 건지 정의 |
doAnswer | 스터빙 메소드 호출 후 작업을 할지 커스텀하여 정의 |
doNothing | 스터빙 메소드 호출 후 어떤 행동도 하지 않게 정의 |
doCallRealMethod | 실제 메소드 호출 |
// {Stubber 메소드}.when({stubbing 할 클래스}).{stubbing 할 메소드};
@Test
void test_thenReturn() {
// Given
List<?> list = new ArrayList<>();
List<?> sot = spt(list);
// When
// Then
doReturn("test").when(spy).get(0); // spy.get(0)은 "test"를 return
assertEquals("test", spy.get(0));
}