진행 중인 Spring Boot
프로젝트가 로컬에서 테스트를 돌리면 성공하는데 Github Action CI
는 통과하지 못하는 오류가 발생한다는 팀원의 얘기를 듣고 해결을 도와주기로 했다.
Github Action CI
에서는 NoSuchElement
오류 로그를 출력하고 있었다. 원인을 파악해보니 @BeforeEach
어노테이션, findById()
그리고 @GeneratedValue(strategy=GenerationType.IDENTITY)
때문에 복잡하게 발생한 오류였다. 상황을 정리하면 다음과 같다.
@BeforeEach
어노테이션이 붙은 테스트 데이터 저장 함수가 모든 테스트함수마다 실행한다.Transaction
으로 롤백했지만, 자동 생성되는 아이디들의 시작 값은 롤백되지 않는다. 즉, 이후에 데이터를 데이터베이스에 저장하면 자동 생성된 아이디들의 시작값은 1이 아니라는 것이다.상황을 파악하고 나서 그렇다면 @BeforeEach
가 아니라 @BeforeAll
어노테이션을 사용해서 테스트 데이터 저장 함수를 테스트 클래스에서 딱 한번만 실행하면 되지 않을까 생각했다. 위에서 얘기했듯이 지금의 문제는 똑같은 테스트 데이터들을 저장하는 함수가 @BeforeEach
어노테이션 때문에 모든 테스트 함수마다 실행되고 그로 인해 테스트 데이터들의 아이디가 예상한 아이디와 달라서 테스트 코드가 오류가 나는 것이기 때문이다. 테스트 클래스에서 딱 한번만 실행하면 테스트 함수 코드 작성 시 예상한 데이터의 아이디와 같기 때문에 오류가 안날 것이다. 그래서 @BeforeAll
어노테이션을 테스트 데이터 저장 함수에 사용했다.
로컬에서는 오류가 발생하지 않아서 Github
로 푸시했으나 이번엔 AssertionError
를 발생시켰다. 원인을 다시 파악해봤다. 생각해보니 Github
는 테스트 클래스를 전부 다 한번에 실행해서 테스트를 한다. 하지만 나는 로컬에서 테스트 클래스마다 실행해보면서 테스트를 진행했다. 상황을 정리하면 다음과 같다.
Github Action CI
는 테스트 클래스를 전부 한번에 실행해서 테스트를 진행한다. 위에서 얘기했듯이 Transaction
롤백을 해도 데이터베이스에 반영된 데이터는 지우지만, 자동 생성 아이디의 시작값은 롤백하지 않는다. Github Action CI
는 테스트 클래스를 전부 한번에 실행하기 때문에 테스트 DB인 H2에서 자동 생성되는 아이디는 클래스 단위로 다시 1부터 자동 생성되는 것이 아니라는 것이다. 위와 같은 상황을 파악하고 findById()
가 아니라 findAll()
을 사용해서 원하는 기능의 테스트 코드를 작성했다. 그리고 나서 나도 모든 테스트 클래스 한 번에 실행했고, 테스트를 통과해서 Github
에 푸시하니 오류 없이 CI/CD
가 잘 작동했다.
결론적으로 데이터베이스에 저장된 데이터의 아이디가 예상한 아이디와 달랐고 예상한 아이디를 바탕으로 짠 테스트 코드가 문제였다. 앞으로는 findById()
로 테스트 코드를 짤 때 데이터의 아이디를 함부로 예상하지 말고 findAll()
이나 다른 방법들을 저장된 데이터의 아이디를 한번 확인해보고 테스트 코드를 짜는 것이 안정적이라는 것을 깨달았다.
추가로 Github Action CI
의 테스트 실행 단위도 알 수 있었다.