diff --git a/src/test/java/com/backend/server/config/ControllerTest.java b/src/test/java/com/backend/server/config/ControllerTest.java index fd003fee..aae0484f 100644 --- a/src/test/java/com/backend/server/config/ControllerTest.java +++ b/src/test/java/com/backend/server/config/ControllerTest.java @@ -1,26 +1,27 @@ package com.backend.server.config; -import jakarta.transaction.Transactional; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; /** - * 로그인을 자동으로 해주어, 컨트롤러 레이어의 테스트를 쉽게 할수 있게 해주는 어노테이션입니다.
- * dev 서버용 인증필터를 사용해서, 요청에 JWT 토큰이 없어도 관리자 계정으로 자동으로 인증됩니다.
+ * 실제 요청을 통한 테스트를 쉽게 작성하게 해주는 어노테이션입니다.
+ * 1. 새로운 DB를 생성 / 삭제하여 테스트를 격리합니다.(테스크 클래스 마다)
+ * 2. @Transactional 을 이용하여 테스트 메소드마다 DB 변경사항을 롤백합니다.
+ * 3. 요청에 JWT 토큰이 없어도 관리자 계정으로 자동으로 인증됩니다.
+ * 4. api rate limit 제한을 해제합니다. (분당 5회 호출 제한 등등을 해제)
+ * (인증을 위해 관리자 계정을 DB에 추가합니다.) */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited -@Transactional -@SpringBootTest +@DatabaseTest @AutoConfigureMockMvc @ActiveProfiles("dev") @Import({MockMvcConfig.class, FakeRateLimitConfig.class, TestDatabaseConfig.class}) diff --git a/src/test/java/com/backend/server/config/DatabaseTest.java b/src/test/java/com/backend/server/config/DatabaseTest.java index 89c5e437..6e7f908e 100644 --- a/src/test/java/com/backend/server/config/DatabaseTest.java +++ b/src/test/java/com/backend/server/config/DatabaseTest.java @@ -11,8 +11,11 @@ /** * DB 를 사용하는 테스트는 이 어노테이션을 쓰면 됩니다.
- * 1. 새로운 DB를 생성 / 삭제하여 테스트를 격리합니다. - * 2. @Transactional 을 통해 DB 변경사항들을 롤백합니다. + * 1. 새로운 DB를 생성하여 테스트를 격리합니다.(테스크 클래스 마다)
+ * 2. @Transactional 로 테스트 메소드마다 DB 변경사항을 롤백합니다. + * @see + * Junit5 로 병렬 테스트를 할 경우 @DirtiesContext 를 사용해선 안됩니다. (공식문서) + * */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/test/java/com/backend/server/config/TestDatabaseConfig.java b/src/test/java/com/backend/server/config/TestDatabaseConfig.java index 9c18c633..107cf1f6 100644 --- a/src/test/java/com/backend/server/config/TestDatabaseConfig.java +++ b/src/test/java/com/backend/server/config/TestDatabaseConfig.java @@ -1,24 +1,31 @@ package com.backend.server.config; -import static com.backend.server.support.TestDatabaseUtil.TestDatabaseSettings.URL_TEMPLATE; +import static com.backend.server.support.TestDatabaseUtil.PASSWORD; +import static com.backend.server.support.TestDatabaseUtil.URL_TEMPLATE; +import static com.backend.server.support.TestDatabaseUtil.USER; import com.backend.server.support.TestDatabaseUtil; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import javax.sql.DataSource; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; -import org.springframework.test.context.DynamicPropertyRegistrar; +import org.springframework.context.annotation.Primary; -/** - * 테스트 격리를 위한 db 를 생성 / 삭제합니다. - */ @TestConfiguration public class TestDatabaseConfig { @Bean - DynamicPropertyRegistrar apiPropertiesRegistrar() { - return registry -> { - registry.add(URL_TEMPLATE.getYmlPath(), - () -> URL_TEMPLATE.getValue() + TestDatabaseUtil.createTestDatabase()); - registry.add("spring.jpa.hibernate.ddl-auto", () -> "create"); - }; + @Primary + public DataSource dataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl(URL_TEMPLATE + TestDatabaseUtil.createTestDatabase()); + config.setUsername(USER); + config.setPassword(PASSWORD); + config.setDriverClassName("org.postgresql.Driver"); + config.setMaximumPoolSize(5); + config.setPoolName("TestHikariPool"); + + return new HikariDataSource(config); } } diff --git a/src/test/java/com/backend/server/support/TestDatabaseUtil.java b/src/test/java/com/backend/server/support/TestDatabaseUtil.java index 79699e40..23be91b4 100644 --- a/src/test/java/com/backend/server/support/TestDatabaseUtil.java +++ b/src/test/java/com/backend/server/support/TestDatabaseUtil.java @@ -1,9 +1,5 @@ package com.backend.server.support; -import static com.backend.server.support.TestDatabaseUtil.TestDatabaseSettings.PASSWORD; -import static com.backend.server.support.TestDatabaseUtil.TestDatabaseSettings.URL; -import static com.backend.server.support.TestDatabaseUtil.TestDatabaseSettings.USER; - import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; @@ -12,24 +8,15 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; -import lombok.AllArgsConstructor; -import lombok.Getter; public class TestDatabaseUtil { - @AllArgsConstructor - @Getter - public enum TestDatabaseSettings { - URL("spring.datasource.url", "jdbc:postgresql://localhost:5432/cec"), - URL_TEMPLATE("spring.datasource.url", "jdbc:postgresql://localhost:5432/"), - DRIVER("spring.datasource.driver-class-name", "org.postgresql.Driver"), - USER("spring.datasource.username", "cec"), - PASSWORD("spring.datasource.password", "1234"), - DB_NAME("test.db.name", "dbName"); - final String ymlPath, value; - } + public static final String URL_TEMPLATE = "jdbc:postgresql://localhost:5432/"; + public static final String USER = "cec"; + public static final String PASSWORD = "1234"; - public static final String PREFIX = "test_db_"; + private static final String URL = "jdbc:postgresql://localhost:5432/cec"; + private static final String PREFIX = "test_db_"; private static final ConcurrentMap dbNames = new ConcurrentHashMap<>(); private static final Random random = new Random(); @@ -42,7 +29,7 @@ public static String createTestDatabase() { final String dbId = String.valueOf(random.nextLong(0L, Long.MAX_VALUE)); final String dbName = PREFIX + dbId; - try (Connection conn = DriverManager.getConnection(URL.getValue(), USER.getValue(), PASSWORD.getValue()); + try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); Statement stmt = conn.createStatement()) { stmt.executeUpdate("CREATE DATABASE \"" + dbName + "\""); System.out.println(dbName + " 생성 성공."); @@ -50,7 +37,7 @@ public static String createTestDatabase() { throw new RuntimeException("DB 생성 실패: " + dbName, e); } - dbNames.put(dbName, "true"); + dbNames.put(dbId, dbName); if (!isTriggered.get()) { isTriggered.set(true); Runtime.getRuntime().addShutdownHook(new Thread(TestDatabaseUtil::dropAllTestDatabases)); @@ -62,9 +49,9 @@ public static String createTestDatabase() { * 아직 삭제되지 않은 db 들을 전부 삭제합니다. */ public static void dropAllTestDatabases() { - try (Connection conn = DriverManager.getConnection(URL.getValue(), USER.getValue(), PASSWORD.getValue()); + try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); Statement stmt = conn.createStatement()) { - for (String dbName : dbNames.keySet()) { + for (String dbName : dbNames.values()) { try { stmt.executeUpdate("DROP DATABASE IF EXISTS \"" + dbName + "\" WITH ( FORCE )"); System.out.println(dbName + " 삭제 성공."); diff --git a/src/test/resources/junit-platform.properties b/src/test/resources/junit-platform.properties index a69c0f0f..2a882a6c 100644 --- a/src/test/resources/junit-platform.properties +++ b/src/test/resources/junit-platform.properties @@ -1,2 +1,8 @@ junit.jupiter.displayname.generator.default = \ - org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores \ No newline at end of file + org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores + +# https://docs.junit.org/current/user-guide/#writing-tests-parallel-execution +# https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/parallel-test-execution.html +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.mode.classes.default = concurrent +junit.jupiter.execution.parallel.mode.default = same_thread \ No newline at end of file