안녕하세요.
지금까지 인강을 통해서 만들어온 프로젝트를 복습할 겸, 이것저것 소스도 둘러보고, 웹앱도 잘 동작하는지 테스트도 하고, 그리고 단위 테스트도 전부 돌려보는 시간을 가졌습니다. 그런데... 단위 테스트에 엄청난 오류가 발생하고 있었습니다.
에러 메시지
java.lang.IllegalStateException: Unable to create SecurityContext using @com.studyolle.WithAccount(value="devkis")
Caused by: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["PUBLIC.UK_Q0UJA26QGU1ATULENWUP9RXYR_INDEX_E ON PUBLIC.ACCOUNT(EMAIL) VALUES 93"; SQL statement: insert into account (alarm_apply_result_to_email, alarm_apply_result_to_web, alarm_study_creation_to_email, alarm_study_creation_to_web, alarm_update_info_to_email, alarm_update_info_to_web, bio, email, email_check_token, email_check_token_generated_at, email_verified, joined_at, nickname, occupation, password, personal_url, profile_img, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [23505-200]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:298)
에러는 단위 테스트 메서드가 실행되기 전에 디비에 계정을 새로 생성하고 로그인까지 되도록 하는 어노테이션에서 발생하고 있었습니다.
계정을 디비에 넣는 insert 쿼리에서 에러가 발생하고 있었고, PUBLIC.UK가 나온 걸로 봐서는 중복된 데이터가 들어가서 발생한 에러라고 판단이 되었습니다.
저는 스프링부트를 사용하고 있고 얼마 전, 프로젝트가 실행되면 인메모리 디비도 사용하고 postgreSQL도 사용되도록 application.properties와 application-dev.properties를 두 개 만들어서 프로젝트에 적용했습니다. 문제의 원인은 여기에 있었습니다.
application.properties | application-dev.properties |
- 인메모리 디비를 사용하고, - spring.jpa.hibernate.ddl-auto=create-drop 로 설정하였습니다. - 프로젝트가 생성될 때 스키마가 생성되고, 그 테이블에 데이터가 담깁니다. 프로젝트를 종료하게 되면 데이터는 날라갑니다. |
- postgreSQL를 사용하고, - spring.jpa.hibernate.ddl-auto=update 설정하였습니다. - 프로젝트 실행과 종료에 관계없이, 스키마도 그대로 있고 변경된 부분만 방영이 됩니다. 데이터도 그대로 존재하게 됩니다. |
단위 테스트 메서드가 실행될 때마다, 매번 계정을 새로 생성을 해주도록 새로 어노테이션을 만들어서 적용했었습니다. 매번 계정이 생성돼서 로그인이 된 상태에서 테스트를 하는 이유는 로그인된 유저가 자신의 계정의 정보를 수정하는 서비스를 테스트해야 했기 때문입니다.
에러 원인
계정 스키마에서는 "닉네임" 컬럼은 중복 불가능한 Uniqe 한 스키마입니다. 그런데, 이미 PostgreSQL에 존재하는 닉네임으로 단위 테스트에서 계정을 생성하려고 하니 발생하는 단순한 중복 발생 문제였습니다. 그래서 단위테스트에서 로그인이 된 사용자를 대상으로 하는 서비스에서만 에러가 발생하고 있었습니다. 가령, 계정의 정보를 변경하는 기능입니다.
근데, 의문이 있습니다. 그리고 이 의문은 뭔가 이번 이슈의 가장 핵심이 되는 중요한 내용일 것만 같습니다.
왜?!,
단위 테스트가 바라보는 properties가 인메모리 디비로 설정된 application.properties가 아니라 postgreSQL를 사용하도록 datasource가 설정된 application-dev.properties를 바라보고 있는 걸까요?? 아무런 설정도 하지 않은 상태에서 JUnit은 왜 application-dev.application을 우선적으로 선택한 걸까요??
...
해결방법
해결 방법은 두 가지가 있다고 생각했습니다. 두 가지 방법 모두 단위테스트가 바라보는 properties를 인메모리 디비가 설정된 properties를 바라보게 해주는 방법입니다.
저는 JUnit5를 사용하고 있습니다.
첫 번째 방법은, 단위테스트 클래스에 @Profile어노테이션으로 application.properties를 로컬용(인메모리 디비가 설정된..)으로 직접 선언해 주는 방법입니다. (제가 생각하기에는 가장 추천되는 방법입니다.)
방법은 다음과 같습니다.
1. application.properties에 spring.profiles.active=local 설정을 해줍니다.
2. 단위 테스트의 클래스에 @Profile("local") 어노테이션을 선언해 줍니다.
두 번째 방법은, @SpringbootTest(properties="classpath:application.properties)를 선언하는 것입니다.
이상으로, 단위 테스트에서 개발용 실제 DB와 인메모리 디비를 설정하는 방법을 알아보았습니다.
감사합니다.
'SpringBoot > 테스트코드' 카테고리의 다른 글
JUnit5에서 의존성 주입은 @Autowired로 해야하는 이유 (7) | 2020.09.10 |
---|---|
단위테스트에서 FK관계에서 발생한 DataIntegrityViolationException 에러 (0) | 2020.08.20 |
[Springboot 단위테스트]MockMvc Bean을 주입받지 못하는 에러 (0) | 2020.07.26 |
JUnit4와 JUnit5의 차이점. (0) | 2020.07.19 |
[SecurityTest]로그인 인증 테스트 방법 (0) | 2020.07.19 |
댓글