Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/test/java/com/backend/server/config/ControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
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;

/**
* 로그인을 자동으로 해주어, 컨트롤러 레이어의 테스트를 쉽게 할수 있게 해주는 어노테이션입니다.<br>
* dev 서버용 인증필터를 사용해서, 요청에 JWT 토큰이 없어도 관리자 계정으로 자동으로 인증됩니다.<br>
* 실제 요청을 통한 테스트를 쉽게 작성하게 해주는 어노테이션입니다.<br>
* 1. 새로운 DB를 생성 / 삭제하여 테스트를 격리합니다.(테스크 클래스 마다)<br>
* 2. @Transactional 을 이용하여 테스트 메소드마다 DB 변경사항을 롤백합니다.<br>
* 3. 요청에 JWT 토큰이 없어도 관리자 계정으로 자동으로 인증됩니다.<br>
* 4. api rate limit 제한을 해제합니다. (분당 5회 호출 제한 등등을 해제)<br>
* (인증을 위해 관리자 계정을 DB에 추가합니다.)
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Transactional
@SpringBootTest
@DatabaseTest
@AutoConfigureMockMvc
@ActiveProfiles("dev")
@Import({MockMvcConfig.class, FakeRateLimitConfig.class, TestDatabaseConfig.class})
@Import({MockMvcConfig.class, FakeRateLimitConfig.class})
@TestExecutionListeners(
listeners = {UserSaveListener.class},
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
Expand Down
7 changes: 5 additions & 2 deletions src/test/java/com/backend/server/config/DatabaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@

/**
* DB 를 사용하는 테스트는 이 어노테이션을 쓰면 됩니다.<br>
* 1. 새로운 DB를 생성 / 삭제하여 테스트를 격리합니다.
* 2. @Transactional 을 통해 DB 변경사항들을 롤백합니다.
* 1. 새로운 DB를 생성하여 테스트를 격리합니다.(테스크 클래스 마다)<br>
* 2. @Transactional 로 테스트 메소드마다 DB 변경사항을 롤백합니다.
* @see <a href="https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/parallel-test-execution.html">
* Junit5 로 병렬 테스트를 할 경우 @DirtiesContext 를 사용해선 안됩니다. (공식문서)
* </a>
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
Expand Down
29 changes: 18 additions & 11 deletions src/test/java/com/backend/server/config/TestDatabaseConfig.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
31 changes: 9 additions & 22 deletions src/test/java/com/backend/server/support/TestDatabaseUtil.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<String, String> dbNames = new ConcurrentHashMap<>();
private static final Random random = new Random();
Expand All @@ -42,15 +29,15 @@ 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 + " 생성 성공.");
} catch (SQLException e) {
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));
Expand All @@ -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 + " 삭제 성공.");
Expand Down
8 changes: 7 additions & 1 deletion src/test/resources/junit-platform.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
junit.jupiter.displayname.generator.default = \
org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores
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