인프런 커뮤니티 질문&답변

asdasfafsf님의 프로필 이미지
asdasfafsf

작성한 질문수

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

JSP로 회원 관리 웹 애플리케이션 만들기

@BeforeEach 동작

해결된 질문

작성

·

609

·

수정됨

0

[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)

[질문 내용]
안녕하세요. 강사님의 강의를 듣고 Spring Boot로 웹 페이지 개발을 연습중입니다.
TDD방식으로 구현을 하며 웹 페이지를 만들고 있습니다.

환경

IDE: IntelliJ

pc: MacBook Air 2022

Spring Boot Version: 3.1.2

소스파일: https://drive.google.com/file/d/17oeGgTCTbRMAtcaxrE4sWDtw7NRTJlPk/view?usp=sharing

구현

테스트환경을 독립적으로 구성하고 싶어서 @BeforeEach를 사용하여 테스트 코드 실행 전에 H2 db 인메모리에 sql스크립트를 실행하여 데이터를 세팅하는 작업을 하고 테스트들을 진행 중에 있습니다.

import org.springframework.jdbc.core.JdbcTemplate;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
@BeforeEach
void dbConfig() {
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    ClassPathResource schema = new ClassPathResource("schema.sql");
    try {
        ScriptUtils.executeSqlScript(jdbcTemplate.getDataSource().getConnection(), schema);
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }
    ClassPathResource data = new ClassPathResource("data.sql");
    try {
        ScriptUtils.executeSqlScript(jdbcTemplate.getDataSource().getConnection(), data);
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }
}

증상

데이터를 Insert후에 반환된 기본 키 값으로 Select하여 검증을 하는 로직에서 단독 테스트시에는 정상적으로 잘 동작하나 test 패키지를 전체 테스트를 돌리면 Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "board" not found; SQL statement: 이와 같이 테이블을 찾을 수 없다고 간헐적으로 오류가 발생하고 있습니다.


[시도해본 방법]

  1. SQL스크립트가 제대로 로드 되지 않았다고 생각되어 테이블 생성 스크립트 후에 Thread.sleep();을 줘서 지연을 줘봤지만 실패

  2. 기존에는 ScriptUtils.executeSqlScript(jdbcTemplate.getDataSource().getConnection(), schema); 실행을 schema와 data를 연달아 수행하던 것을 schem먼저 수행 후, data수행하도록 변경했지만 실패

  3. Chat-GPT에 질문을 해보니 @BeforeEach를 사용하여 데이터베이스 스키마를 초기화하고 데이터를 삽입하는 경우, 테스트 메소드 간에 순서가 보장되지 않을 수 있기 때문에, 특정 테스트 메소드가 실행되기 전에 데이터베이스 초기화가 이루어지지 않을 수 있습니다. 라고 답변을 받았는데요. 그래서 @Order 어노테이션으로 @Test 메소드들의 순서를 지정해봤는데도 동일한 오류가 발생합니다.

이와 같이 잘 안되고 있어 문의 드립니다.

답변 2

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. asdasfafsf님

소스 파일이 열리지 않습니다. 공개로 변경해주세요.

감사합니다.

asdasfafsf님의 프로필 이미지
asdasfafsf
질문자

답변 늦어져서 죄송합니다.
소스 링크 공개로 변경하였습니다.

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. asdasfafsf님

우선 저는 여러번 실행해보았지만 문제가 발생하지 않았습니다.

코드를 봐도 특별히 문제가 보이지 않는데요.

혹시 관련해서 비슷한 이슈를 겪어보신 분이 있다면 답변 부탁드립니다.

감사합니다.

asdasfafsf님의 프로필 이미지
asdasfafsf
질문자

안녕하세요 강사님.
위 구글 드라이브 링크 내 소스를 지인 한 명한테 테스트요청 하였고 동일한 오류가 발생하기는 했습니다. 지인 테스트 환경은 MacBook M2 Air 15인치 입니다.
프로젝트내에 gradlew파일 ./gradlew clean build 명령어로 빌드를 시도 해봐도 같은 증상이 나타나는데요. 빌드 실패시 리포트 파일 공유 드립니다.
https://drive.google.com/drive/folders/1pDu4XFENpOSGGLJPvyo-5TzdwubCZPyR?usp=sharing

김영한님의 프로필 이미지
김영한
지식공유자

저도 정확한 원인은 모르겠지만, 지금 현상으로만 보아서는 H2 데이터베이스 내부에 이슈가 있어서 테이블을 지우고 다시 생성하는 부분을 따라가지 못하는 것 같습니다. 다음과 같이 조치해보시겠어요?

  1. DriverManagerDataSource를 BeforeEach에서 항상 생성하도록 만든다.

  2. jdbc:h2:mem:test에서 test라는 이름을 랜덤으로 만든다. 그래서 모든 테스트가 다른 데이터베이스 이름에 접근하도록 한다.

asdasfafsf님의 프로필 이미지
asdasfafsf
질문자

말씀해주신대로 기존에 @BeforeAll에서 한 번만 설정하던 로직에서 @BeforeEach로 항상 생성하도록 변경하였고, 데이터베이스명 랜덤하게 바꾸는 것은 두 가지 방법으로 테스트하였으나 동일한 오류가 발생하고 있습니다.
진행했던 테스트 방식은 다음과 같습니다. 첫 번째는 기존 "test"명 뒤에 랜덤한 숫자를 붙이는 방식이고, 두 번째는 그냥 랜덤한 숫자를 붙이는 방식입니다.
아래는 두가지 방식에 대한 로그입니다. 키워드는 database name:로 검색하시면 볼 수 있습니다.
https://drive.google.com/file/d/1mRKd2t3NiHeyolW28wsry4FYbafHLzh8/view?usp=sharing (test명 뒤에 랜덤한 숫자)
https://drive.google.com/file/d/1UhURReIXtuZiNljZ3wrd744KiV07FlN3/view?usp=sharing (랜덤한 숫자)

김영한님의 프로필 이미지
김영한
지식공유자

로그를 봤는데, 로그를 보면 문제없이 잘 수행되어야 합니다.

H2 내부에서 뭔가 문제가 있는 것 같아요.

혹시 모르니 다음 옵션을 제거해보시겠어요?

;DATABASE_TO_LOWER=TRUE
asdasfafsf님의 프로필 이미지
asdasfafsf
질문자

안녕하세요 강사님.
키워드를 JUnit5 h2 memory mode "Table not Found" 로 다시 검색하여 해결하였습니다.(링크)
DB_CLOSE_DELAY 옵션을 -1로 변경하여 테스트가 오류없이 실행되었습니다.(H2)

김영한님의 프로필 이미지
김영한
지식공유자

asdasfafsf님 저도 궁금했는데, 잘 찾아서 해결하셨네요 :) 좋은 내용 공유해주셔서 감사합니다.

실무에서는 보통 커넥션 풀을 사용하기 때문에 이런 문제가 발생하지 않았지만, 그렇지 않은 경우에는 주의해야겠네요.

asdasfafsf님의 프로필 이미지
asdasfafsf
질문자

아 그러면 실무에서 테스트를 독립적으로 수행하기 위해서 DB연결을 어떤식으로 하고 있나요? 예를들어 @SpringBootTest를 사용하여 테스트 진행시 "src/test/resources" 경로 밑에 테스트용 application.properties를 넣는식으로 하나요?

김영한님의 프로필 이미지
김영한
지식공유자

실무에서는 상황에 따라서 다르겠지만, DB를 연결하는 테스트의 경우 @SpringBootTest를 함께 사용하는 경우가 많습니다 :)

0

안녕하세요, 인프런 AI 인턴이에요. @BeforeEach는 JUnit 프레임워크에서 제공하는 어노테이션으로, 각각의 테스트 메소드가 실행되기 전에 먼저 실행되어야 하는 코드를 작성할 수 있게 해줍니다.

위의 코드에서 @BeforeEach 어노테이션을 사용하여 테스트 코드 실행 전에 H2 db 인메모리에 sql 스크립트를 실행하여 데이터를 세팅하는 작업을 하고 있습니다.

JdbcTemplate은 스프링 프레임워크에서 제공하는 JDBC 추상화 계층입니다. 이를 사용하여 데이터베이스와 상호작용할 수 있습니다.

ClassPathResource는 클래스 패스에 있는 리소스를 표현하는 클래스입니다. “schema.sql” 파일을 참조하여 SQL 스크립트를 가져옵니다.

ScriptUtils.executeSqlScript를 사용하여 JdbcTemplate의 데이터 소스에 연결된 Connection을 가져와서 “schema.sql” 파일에 있는 SQL 스크립트를 실행합니다.

이렇게 테스트 코드 실행 전에 데이터베이스를 초기화하는 작업을 수행하면, 각각의 테스트 메소드가 실행될 때마다 일관된 상태에서 테스트를 진행할 수 있습니다.

테스트 코드를 작성하는 과정에서 데이터베이스 초기화가 필요한 경우에 @BeforeEach 어노테이션을 사용하여 초기화 작업을 수행할 수 있습니다.

더 자세한 내용은 JUnit 공식 문서를 참고해주세요. 좋은 공부 되세요!

asdasfafsf님의 프로필 이미지
asdasfafsf
질문자

@BeforeEach에 대한 가능을 사용하여 매 테스트마다 데이터베이스의 테이블을 새로 세팅하고 싶어서 설계를 저렇게 했는데요.

전체 테스트를 진행시에 간헐적으로 테이블이 없다는 sql문법 에러문구를 발견해서 보통 테스트를 구성 할 때 인메모리 데이터베이스에 sql스크립트 파일을 어떤식으로 올리는지 궁금합니다.

asdasfafsf님의 프로필 이미지
asdasfafsf

작성한 질문수

질문하기