spock 통합테스트를 하려는 과정이다.
1.
gradle로 실행하는 것과 intellij IDEA(junit) 으로 실행하는게 있다.
(sttings - gradle - run test using 에서 둘중에 하나 고르기)
하지만 gradle로 테스트하면 edit configuration에 세팅한 VM options 를 못 읽는다.
VM options에 jasypt.encryptor.password 를 세팅해놨는데 못읽으니 무용지물이다.
DB 접속정보 jasypt로 암호화해놓음.
그렇다고 build.gradle에 jasypt.encryptor.password 를 박아넣고 push 할 수는 없지 않은가..
gradle 로 하면 reporting도 해줘서 index.html로 결과를 볼 수 있으니 좋긴 하다만 어쩔 수 없다.
intellij IDEA(junit) 로 테스트하자.
이걸로도 결과 파일 뽑을 수 있다.
export test results 누르면 된다.
근데 이걸로 html 다운받으면 희한하게 springboot 로그를 같이 출력한다.. 왜 그렇게 만들었냐 JetBrains
그래서 인터넷 뒤져보다가 github에서 이 html을 만드는 xsl 파일을 찾았다. (올려주신분 압도적 감사)
그래서 그 xsl 파일을 수정해서 로그 출력 안하게 하고, 글씨 크기 바꾸고 깔끔하게 바꿨다.(한줄로 간단하게 썼지만 이 과정도 매우 오래걸렸다.. 어디서 어떻게 출력하는지 명시되어있지 않고 숨겨져 있어서 찾는데 한참 걸림)
export test results 할 때 custom, apply XSL template 선택해서 이 수정한 xsl로 뽑으면 된다.
2.
나는 특정 조건에 따라 테스트 클래스에 @SpringBootTest, @DataJpaTest 둘 중에 하나를 적용하고 싶었다.
@Conditional 은 스프링 컨텍스트가 확인하는 것이기 때문에 스프링 컨텍스트가 생성된 이후에 동작한다.
@SpringBootTest, @DataJpaTest 이것들이 스프링 컨텍스트를 만드는 어노테이션인데 스프링 컨텍스트가 없는 시점에 이걸 조건적으로 적용하고 싶은거니까 @Conditional 은 못쓴다.
컴파일타임에 어노테이션이 고정된다(소스코드가 정해진다)
junit의 @ExtendWith 으로 조건적으로 액티브 프로필을 적용할 순 있다.
@ExtendWith(ConditionalTestExtension.class) 으로 쓰고
ConditionalTestExtension 파일 만들어서 다른 어노테이션의 falg를 읽던, 변수를 읽던 해서 조건 만듦.
하지만 프로필을 여러개 만들어야 하고 각각의 프로필을 사용할 테스트 클래스 또한 여러개 만들어야 한다.
(@ActiveProfiles("AAA"), @SpringBootTest 붙은 클래스 하나, @ActiveProfiles("BBB"), @DataJpaTest 붙은 클래스 하나)
똑같은 코드를 여러개 만드는건 불필요하다.
@ExtendWith는 스프링 컨텍스트가 생성되기 전에 동작하기 때문에 스프링 컨텍스트 없이도 된다.
1. 컴파일타임
- 어노테이션 고정
2. 런타임
- @ExtendWith 확인
- 정해진 프로필에 따라 실행할 테스트 클래스 결정
- 클래스에 적용된 어노테이션에 따라 스프링 컨텍스트를 띄우거나 안띄우거나
컴파일타임에 어노테이션을 동적으로 처리하는 방법은 Annotation Processor 를 사용하는 것이다.
위에 썼듯 컴파일타임에 어노테이션이 고정되는건데 Processor 가 먼저 돌아서 조건적으로 어노테이션 정하는 것
Annotation Processor 동작 과정
컴파일타임. @ABCTest를 감지해서 값에 따라 어노테이션 추가하게 작성해놓음
1. 자바 컴파일러가 소스코드를 컴파일할 때 @ABCTest 애노테이션을 발견
2. SpockTestProcessor가 활성화되어 process() 메서드 실행
3. @ABCTest 의 flag 값에 따라
- false면 @SpringBootTest 추가
- true면 @DataJpaTest 추가
런타임에는 이미 @SpringBootTest가 추가된 상태로 시작
이후 스프링 테스트 프레임워크는 추가된 @SpringBootTest를 기반으로 스프링 컨텍스트 구성
Lombok(롬복)이 이런식으로 동작
@SpringBootTest 애노테이션 처리 과정
● 컴파일 타임
1. Java 컴파일러가 바이트코드로 변환하기 전에 Annotation Processor 동작 (있으면)
2. Java 컴파일러가 소스 코드를 바이트코드로 변환
3. 애노테이션의 문법 검사만 수행
4. @SpringBootTest 등의 애노테이션은 메타데이터로 클래스 파일에 저장
● 런타임 - JUnit 테스트 실행 시
1. JUnit Platform이 테스트 클래스 로드
2. @ExtendWith(SpringExtension.class)에 의해 SpringExtension이 활성화
- @SpringBootTest에 기본으로 적용되어 있어서 동작하는 @ExtendWith (얘도 있으면 동작)
3. Spring TestContext Framework 동작
- TestContextManager가 테스트 컨텍스트 관리
- @SpringBootTest 애노테이션 감지
4. SpringBootTestContextBootstrapper 동작
- 스프링 부트 테스트에 필요한 설정 로드
- ApplicationContext 생성 및 구성
5. 애플리케이션 컨텍스트 초기화
settings - Annotation Processors 에 Enable annotation processing 체크하래서 했는데 왜 안돼...
-> 성공한 현재 안됐던 이유 파악 : 이 때 만들었던 Annotation Processor는 자바 컴파일러에서 동작하는 것이다. 나는 groovy 테스트코드를 실행했고 때문에 그루비컴파일러가 동작했기 때문에 Annotation Processor가 적용되지 않았다.
빌드 과정을 보면 그루비 컴파일러가 동작한 것을 확인할 수 있다.
런타임에 수정하기
@ExtendWith 은 클래스가 JVM에 로드되기 전에 실행되므로 바이트코드 수정 가능
Java Agent : JVM이 바이트코드 실행 전에 premain 메서드를 호출, 바이트코드 조작. 왜 안돼...
-> 성공한 현재 안됐던 이유 파악 : 모르겠음. 돼야할 것 같은데 왜 안됐지.
성공!!
런타임에 Groovy의 AST(Abstract Syntax Tree, 추상 구문 트리) Transformation을 사용하여 어노테이션 추가하기
실행 시점
1. Groovy 컴파일러 동작: Groovy 컴파일러가 소스 코드를 분석하고, AST를 생성하며, 지정된 Transformation을 실행합니다.
2. CANONICALIZATION 단계: 이 단계에서 코드 구조가 정리되고, 클래스 간의 상호 참조가 설정됩니다. 이때 커스텀으로 작성한 Transformation이 호출됩니다.
주요 CompilePhase 설명:
- CompilePhase.INITIALIZATION: 초기화 단계로, AST 트리의 최상위 구조가 설정됩니다. 변수 선언 등의 기본적인 구조가 이 단계에서 이루어집니다.
- CompilePhase.SEMANTIC_ANALYSIS: 의미 분석 단계로, 변수나 메서드의 타입과 같은 유효성 검사를 합니다. 이 시점에서는 코드가 유효한지 분석하지만, 트리의 구조가 아직 확정되지 않은 상태입니다.
- CompilePhase.CANONICALIZATION: 이 시점에서는 AST 트리가 정리되며, 주석, 메서드 호출, 선언된 변수가 최적화되고, 코드가 정리된 후입니다. 변환을 위해 가장 많이 사용되는 시점입니다.
- CompilePhase.CLASS_GENERATION: 이 단계는 실제로 바이트코드를 생성하는 시점으로, 트리가 이미 완성된 후입니다. 이 시점 이후에는 트리 구조가 변경될 수 없습니다.
컴파일 타임(정적)
@GroovyASTTransformation 로 AST 조작
완료 후 Groovy 컴파일러에 의해 동적으로 바이트코드로 컴파일됨
----------------------------------------------
런타임
JUnit Test Runner 실행
JVM이 Groovy 컴파일러가 생성한 .class 파일을 기반으로 테스트 실행
'SpringBoot' 카테고리의 다른 글
호출 순서 TestBootstrapInitializer, TestExecutionListener, setupSpec, ApplicationContextInitializer (0) | 2025.01.10 |
---|---|
spock @LocalServerPort 할당 시점, setup, setupSpec 차이 (0) | 2024.11.20 |
swagger ui 에서 json 이쁘게 출력하기 (springfox 2.9.2 -> 3.0.0) (0) | 2024.10.30 |
@Transactional 롤백 (0) | 2024.07.22 |
[SpringBoot] 필터, 인터셉터 호출 흐름, 적용 방법 (0) | 2024.05.20 |
댓글