티스토리 뷰
Why?
Service 를 테스트 하면서, Service에서 사용하는 Repository를 Mock 하기보다는 인 메모리(H2)로 사용하고 싶어졌다.
물론, Best Practice는 Repository도 Mocking 을 해서 Service 클래스에 주입하는 것이지만, 되는지 궁금하기도 하고
어떻게 되는지 알아보려고한다. 물론, 이 글에서 다루는 내용은 위에 말한 것 자체를 다루지는 않는다.
위 처럼 테스트를 하기 위해서 적절한 Test annotation을 검색해보았는데, 익숙한 @DataJpaTest, @RunWith가 있다.
그런데, 어떤 글에서 @DataJpaTest 는 JPA에 필요한 객체들만 로딩해서 가볍다고 하는데, @RunWith(SpringRunner.class)를 같이 쓰고 있는 것이다.
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserServiceTest {
....
}
그런데, @RunWith(SpringRunner.class) 는 @SpringBootTest 보다 조금 더 가볍다고 듣긴 했는데
어쨋든 대부분의 Bean에 대해 DI를 해주는 등, 꽤나 무거운 작업이고 실제로 경험상 @SpringBootTest랑 크게
속도 차이가 나지 않음을 경험했다.
What
1. @RunWith와 더불어 @RunWith(SpringRunner.class)가 도대체 뭘 하는지?
2. @RunWith와 @SpringBootTest랑 어떻게 다른가?
3. 언제 뭘 써야 할까?
4. @RunWith와 @DataJpaTest를 같이 쓰는것은 Reasonable 한가?
Content
JUnit 클래스나, 부모 클래스가 @RunWith로 어노테이트 되면, 해당 클래스를 default runner가 아닌 test runner를 이용해 JUnit Framework를 호출한다.
@RunWith는 위와 같은 코드로 되어 있으며, value는 반드시 abstract org.junit.runner.Runner의 서브클래스여야 한다.
Runner 클래스는 JUnit test 를 동작시킬 책임이 있고 일반적으로 reflection으로 이 작업을 수행한다.
그래서 정리하자면, @RunWith는 JUnit 의 기본 러너 대신에 사용할 러너를 지정해주는 것이고, 지정된 러너는
JUnit의 Life cycle method 등(@BeforeClass, @AfterClass ..)을 호출하는 책임도 갖고 있다.
그냥 넘어가고 싶지만, "Runner(러너)" 라는 키워드가 계속 신경쓰인다. 감만 있고 제대로 모르고 있다.
JUnit Runner
참고: dzone.com/articles/understanding-junits-runner
JUnit Runner는 JUnit의 추상 Runner class를 상속한 클래스이고. 테스트 클래스들을 실행하는 역할을 한다.
그래서, 실제로 테스트 할 클래스는 제공받은 Runner에게 던져져서, 러너에서 실행시키게 되는 것이다.
여기서 꽤 재밌는 사실이 있는데, 대부분의 IDE는 JUnit Test 결과를 표출해주는데 JUnit Runner에서 구현하는
Descriabable의 getDescription() 메소드에서 내보내는(export) 정보를 이용해서 테스트 결과를 보여준다고 한다.
[1] www.logicbig.com/tutorials/unit-testing/junit/runner.html
이제, Runner는 너무 generous 하기 때문에, 조금 더 구체적인 클래스인 ParentRunner를 살펴보면
BlockJUnit4ClassRunner가 ParentRunner를 상속받는 것을 볼 수 있다. 참고로 BlockJUnit4ClassRunner는
@RunWith로 Runner를 제공해주지 않으면 사용되는 기본 Runner이다.
BlockJUnit4ClassRunner
1. 내부의 getChildren() 메소드를 사용해서 @Test로 어노테이트 된 메소드를 리플렉션을 사용해 몽땅 스캔한다.
최종적으로, runChild() 를 호출해서 테스트 메소드를 실행시킨다.
결론적으로, Runner의 역할은 테스트 실행 프로세스를 계획하고 실행시키는 것이다.
@RunWith(SpringRunner.class)
참고 :
stackoverflow.com/questions/25317009/what-does-this-do-runwithspringjunit4classrunner-class
zetcode.com/spring/springrunner/
SpringRunner는 SpringJUnit4ClassRunner의 단축어이다.
SpringRunenr는 유닛 테스트를 하기전에 Spring container를 시작하는 작업을 포함하기 때문에 오버헤드가 크다
따라서 최대한 절약하며(sparingly) 사용해야 한다.
일반적으로 아래와 같은 상황에 사용한다
1. 컴포넌트가 주입되어야 할 때
2. 설정 데이터가 주입되어야 할 때
그리고 아래와 같은 상황에 사용하는 것을 삼가야 한다.
1. Spring과 관련 없는 기능들을 테스트할 때(SpringRunner는 overhead가 크기 때문에)
testContextManager를 생성하는 코드를 볼 수 있는데, 이 TestContextManager 클래스에 대해서 보면
빨간 밑줄 친 곳을 보면, Spring 의 아주 핵심적인 부분인 ApplicationContext를 로딩하고 접근하는 것을 지원한다고 되어있다.
단, @SpringBootTest를 사용하면 모든 application context를 다 로딩하게 되는데
@RunWith(SpringRunner.class)를 사용한다면, @Autowire, @MockBean에 해당되는 것들에만 application context를
로딩하게 된다.
@RunWith와 @DataJpaTest를 같이 쓰는것은 Reasonable 한가?
일단 @DataJpaTest 를 도대체 왜 써야할까?'
stackoverflow.com/questions/56523416/datajpatest-annotation-usagespring-boot
의 답변에 따르면
@DataJpaTest는 인 메모리 데이터베이스(일반적으로 H2)로 설정하고, @Entity 가 붙은 클래스들을 모조리 스캔한다
그리고, Spring Data JPA repository 도 설정해준다. 또한, Transactional하며 테스트가 끝날 때 rollback 해준다(!)
@DataJpaTest의 코드는 아래와 같다.
쭉 알아보면서 여기까지 도달했지만, 아직 확신이 가지는 않는다.
하지만, 긴 여정이 무색하게 답은 이미 공식 문서에 나와있었다..(ㄴㅇㄱ)
빨간 박스를 해석해보면
JUnit4 를 사용한다면 이 어노테이션은 반드시 @RunWith(SpringRunner.class)와 사용되어야 합니다.
..Reasonable 한지 따질 필요 없이 반드시 같이 써주어야 했군요..
결론
1. @RunWith와 더불어 @RunWith(SpringRunner.class)가 도대체 뭘 하는지?
=> @RunWith JUnit 테스트의 lifecycle 및 테스트가 어떻게 실행시킬 것인지 정의
=> @RunWith(SpringRunner.class)는 @Mock 과 @Autowired등의 기능을 JUnit에서 사용할 수 있도록 해주는 브릿지
2. @RunWith와 @SpringBootTest랑 어떻게 다른가?
=> @RunWith(SpringRunner.class)는 가벼움(일부 Spring 기능만을 사용 및 주입)
=> @SpringBootTest는 ApplicationContext를 모두 적재하기에 시간이 오래걸림(당연히 유닛 테스트에 사용하면 안 됨)
3. 언제 뭘 써야 할까?
JUnit4 라면 @RunWith(SpringRunner.class)와 @DataJpaTest를 같이 써주어야함
4. @RunWith와 @DataJpaTest를 같이 쓰는것은 Reasonable 한가?
JUnit4 라면 @RunWith(SpringRunner.class)와 @DataJpaTest를 같이 써주어야함
그냥 강제로 같이 써줘야 했던 것 임..
이제부터 모든 어노테이션 조합을 시도해가면서 테스트가 성공하길 바라지 않을 수 있을 것 같습니다.
항상..
'프레임워크 > Spring boot' 카테고리의 다른 글
[Spring boot TEST] Mockito input을 그대로 리턴하기 (0) | 2020.11.22 |
---|---|
Cannot instantiate @InjectMocks field named 에러 (0) | 2020.11.16 |
SpringBoot 멀티쓰레드 공부 및 실험 - 1 : jpa locking (5) | 2020.09.13 |
JPA : Caused by: java.sql.SQLSyntaxErrorException (0) | 2020.08.19 |
JPA Test AuditingEntityListener 가 안 될 때 (0) | 2020.07.16 |
- Total
- Today
- Yesterday
- 14714 어플
- aws 프리티어 요금청구
- 함수포인터 오버라이트
- get_filename_component
- CMake probouf
- CMake get_filename_Component
- CMake run proto compiler
- 14714 공부법
- CMake for
- 복습 계획어플
- 14714 복습법
- 14714 어플리케이션
- CMake run protoc
- CMake get file name
- 14714 앱
- 복습 어플
- function pointer overflow
- 14714 review
- 14714 플래너
- CMake 강좌
- CMake 반복문
- CMake for문
- buffer-over-flow
- react-native
- CMake 기초
- CMAke 파일이름 추출
- 토리파 공부법
- 14714 공부법 어플리케이션
- review reminder
- aws 청구문의
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |