1. ๋ฌธ์ ์ํฉ
์น๊ตฌ์ ํ ์ด ํ๋ก์ ํธ๋ฅผ ํ๋ฉฐ ํ ์คํธ๋ฅผ ์ํ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๊ณ ์ @Sql annotation ์ ์ ์ฉํ๊ณ ์ ํ๋ค. ํ์ง๋ง ํ ์คํธ ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ์ง ์๋ ์ด์ ๋ฅผ ์ฐพ๊ธฐ ์ํ ์ฝ์ง ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ๊ณ ์ ํ๋ค.
2. Background
[1] SpringBootTest.WebEnvironment.RANDOM_PORT ์ฌ์ฉ
์ค์ ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ์ ์ฌํ ํ๊ฒฝ์ ์ํด RANDOM_PORT ๋ฅผ ์ค์ ํ๋ค. ํด๋น ์ต์ ์ ์ค์ ํ๋ฉด ํด๋ผ์ด์ธํธ์ ์๋ฒ๊ฐ ๋ถ๋ฆฌ๋์ด ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์ด ๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก Thread Local ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ @Transactional ์ ์ฌ์ฉํ์ฌ ๋กค๋ฐฑ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค.
๋กค๋ฐฑ์ ํด์ผ ํ๋ ์ด์ ๋ ํ ์คํธ ๊ฒฉ๋ฆฌ(Isolated Test) ์ ์ํจ์ด๋ค. ํ ์คํธ ๊ฒฉ๋ฆฌ๋ ๊ณต์ ์์์ ์ฌ์ฉํ๋ ์ฌ๋ฌ ํ ์คํธ๋ผ๋ฆฌ ๊ฒฉ๋ฆฌํ์ฌ ์๋ก ์ํฅ์ ๋ผ์น์ง ์๋ ๋ฐฉ๋ฒ์ ๋งํ์ฌ ๋ํ์ ์ธ ์์๊ฐ DB ๋ฐ์ดํฐ ์ด๋ค. DB ๋ฐ์ดํฐ ๊ฒฉ๋ฆฌ๋ฅผ ์ํด ๋กค๋ฐฑ์ ํ๋ฉฐ, ์คํ๋ง์์๋ ์ผ๋ฐ์ ์ผ๋ก ๋์ผ ์ค๋ ๋์์ ๋์ํ์ฌ @Transactional ์ ์ฌ์ฉํ์ฌ ๋กค๋ฐฑํ๋ค. ํ์ง๋ง ์์ ์ด์ผ๊ธฐํ ๋ฏ, ์ด ๋ฐฉ๋ฒ์ผ๋ก๋ ๋กค๋ฐฑ์ ํ ์ ์์ด RDB ์์ ์ ๊ณตํ๋ TRUNCATE ๋ช ๋ น์ด๋ฅผ ํ์ฉํ๊ณ ์ ํ๋ค.
webEnvironment option in @SpringBootTest
MOCK : Mocking๋ ์น ํ๊ฒฝ์ ์ ๊ณต(MockMvc๋ฅผ ์ฌ์ฉํ ํ ์คํธ ๊ฐ๋ฅ = ๊ฐ์ง ์น ํ๊ฒฝ์์ ํ ์คํธ)
RANDOM_PORT : ์ค์ ์น ํ๊ฒฝ์ ๊ตฌ์ฑ (์ค์ ์น ํ๊ฒฝ(with tomcat) ์์ ํ ์คํธ)
DEFINED_PORT : ์ค์ ์น ํ๊ฒฝ์ ๊ตฌ์ฑ + ์ง์ ํ ํฌํธ์์ ๋์
[2] custom TestExecutionListener ์ฌ์ฉ
sql ํ์ผ์ ํ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์์ง๋ง ์คํค๋ง ๋ณ๊ฒฝ(e.g. ํ
์ด๋ธ ์ถ๊ฐ, ์ญ์ ) ๋๋ง๋ค ์ฝ๋๋ฅผ ์์ ํด์ผ ํ๋ ๋ฒ๊ฑฐ๋ก์์ด ์๊ธฐ ๋๋ฌธ์ INFORMATION_SCHEMA.TABLES(mysql 8.0 ๊ธฐ์ค) ์ ์กด์ฌํ๋ ์คํค๋ง ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ ๋ฐ์ดํฐ๋ฅผ ์ด๊ธฐํํ๋
AcceptanceTestExecutionListener ๋ฅผ ์์ฑํ๋ค.
Spring testContext ๋ ํ ์คํธ๋ฅผ ์ํ ํ์ฅ, ํธ์ ๊ธฐ๋ฅ์ TestExecutionListener ๋ฅผ ํตํด ์ ๊ณตํ๋ค. ์ฆ, ํธ๋ฆฌํ ํ ์คํธ๋ฅผ ์ํ ํธ์ ๊ธฐ๋ฅ์ TestExecutionListener ๋ฅผ ํตํด ์ ๊ณตํ๋ค. ๋ํ์ ์ธ ์์๋ก MockitoTestExecutionListener ์ด ์กด์ฌํ๋ค. @MocbBean ์ด๋ ธํ ์ด์ ์ ์ ์ธํ๊ธฐ๋ง ํ๋ฉด ํ ์คํธ ์ด์ ์ ํด๋น ๋ฆฌ์ค๋์์ ๋ชจํน(mocking) ํ๊ณ ํ ์คํธ๊ฐ ๋๋ ์์ ์์๋ ๋ชจํน์ ์ ๊ฑฐํ๋ค.
import java.sql.Connection;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
public class AcceptanceTestExecutionListener extends AbstractTestExecutionListener {
@Override
public void beforeTestMethod(final TestContext testContext) {
final JdbcTemplate jdbcTemplate = getJdbcTemplate(testContext);
final List<String> truncateQueries = getTruncateQueries(jdbcTemplate);
truncateTables(jdbcTemplate, truncateQueries);
}
private List<String> getTruncateQueries(final JdbcTemplate jdbcTemplate) {
try (Connection connection = jdbcTemplate.getDataSource().getConnection()) {
String schema = connection.getCatalog();
return jdbcTemplate.queryForList(
"SELECT concat('TRUNCATE TABLE ', TABLE_NAME) "
+ "FROM INFORMATION_SCHEMA.TABLES "
+ "WHERE TABLE_SCHEMA = '" + schema + "'", String.class);
} catch (Exception exception) {
throw new RuntimeException();
}
}
private JdbcTemplate getJdbcTemplate(final TestContext testContext) {
return testContext.getApplicationContext().getBean(JdbcTemplate.class);
}
private void truncateTables(final JdbcTemplate jdbcTemplate, final List<String> truncateQueries) {
execute(jdbcTemplate, "SET FOREIGN_KEY_CHECKS = FALSE");
truncateQueries.forEach(v -> execute(jdbcTemplate, v));
execute(jdbcTemplate, "SET FOREIGN_KEY_CHECKS = TRUE");
}
private void execute(final JdbcTemplate jdbcTemplate, final String query) {
jdbcTemplate.execute(query);
}
}
test ์์ฑ ์ ์ถ๊ฐํ๋ property
(1) sql.init.mode : ์คํฌ๋ฆฝํธ ๋์ ์ค์
- ALWAYS: ๋ชจ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ sql ์คํฌ๋ฆฝํธ๋ฅผ ๋์์ํจ๋ค.
(2) spring.jpa.defer-datasource-initialization: true
- 2.5์ด์์ ๋ฒ์ ๋ถํฐ data.sql ์คํฌ๋ฆฝํธ๋ Hibernate๊ฐ ์ด๊ธฐํ๋๊ธฐ ์ ์ ์คํ
- hibernate ddl-auto property ๊ฐ ๋์ํ๊ณ data.sql ์ด ๋์ํ๋๋ก ํ๋ ์ต์
3. ๋ฌธ์ ์ํฉ & ์์ธ
[1] ๋ฌธ์ : ํ ์คํธ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค์ง ์์๋ ๋ฌธ์
ํ ์คํธ๋ฅผ ์ํด ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ @Sql ์ด๋ ธํ ์ด์ ์ ํตํด ์ฃผ์ ํ๊ณ ์ ํ์ง๋ง ๋ฐ์ดํฐ๊ฐ ์ถ๊ฐ๋์ง ์์๋ค.
@DisplayName("์ธ์ฆ ํ
์คํธ")
@Sql("classpath:member.sql")
public class AuthenticationTest {
...
}

[2] ์์ธ : TestExecutionListener ์์๋ก ์ธํ ๋ฌธ์
์์ธ์ TestExecutionListener ์์ ๋ฌธ์ ์๋ค. TestExecutionListener ๋ ๊ฐ๋ฐ์๊ฐ ๋ช ์ํ ์์์ ๋ฐ๋ผ ์คํ๋๋ค. custom TestExecutionListener ์์ฑ์ ๋ณ๋๋ก ์์(Ordered) ๋ฅผ ์ค์ ์ด ์์ผ๋ฉด ์ฐ์ ์์๊ฐ ๊ฐ์ฅ ๋ฎ๊ฒ ์ค์ ๋์ด ๊ฐ์ฅ ๋ฆ๊ฒ ์ํํ๋ค. ๋ฐ๋ฉด, @Sql ์ ๋์์ํค๋ ๋ฆฌ์ค๋์ธ SqlScriptsTestExecutionListener ๋ฅผ ํ์ธํด๋ณด๋ฉด ์์(Ordered)๊ฐ ์ ์ธ๋์ด ์๋ค. ์ฆ, @Sql ์คํํ๊ณ TRUNCATE ๋ก์ง์ด ์คํ๋์ด ๋ฐ์ดํฐ ์ถ๊ฐ๋์ ๋ค์ ์ด๊ธฐํ๋๋ ๋ฌธ์ ์๋ค.
(@Sql ์คํ → ๋ฐ์ดํฐ ๋ชจ๋ ์ ๊ฑฐ(TRUNCATE) ๐ซ = ๋น์ฐํ ๋ฐ์ดํฐ๊ฐ ์๋ค!)

[3] ํด๊ฒฐ : custom TestExecutionListener ์์ ์ ๋ ฅ
TRUNCATE ๋ก์ง ์ดํ์ @Sql ์คํ๋๊ธฐ ์ํด Order ๊ฐ์ SqlScriptsTestExecutionListener ์ ๊ฐ๋ณด๋ค ๋ฎ๊ฒ ์ค์ ํ๋ค. ( TRUNCATE ์คํ → @Sql ์คํ)

์ ์์ ์ผ๋ก ํ ์คํธ ๋ฐ์ดํฐ๊ฐ ๋ฑ๋ก๋์๋ค. ๐
์ค๋์ ๊ตํ
- custom TestExecutionListener ๋ง๋ค ๋๋ ๊ธฐ์กด์ TestExecutionListener ์์ ์์๋ฅผ ๋น๊ตํด์ ๊ฐ์ ์ถ๊ฐํ์.
Reference
'๐ backend > spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| hibernate.query.in_clause_parameter_padding = true ? (1) | 2024.03.06 |
|---|---|
| JPA ์ UUID version 7 ์ ์ฉํ๊ธฐ (0) | 2024.03.04 |
| [Spring] Spring AOP ๋์ ์์๋ณด๊ธฐ (0) | 2024.03.04 |
| [JPA] ์ฝ๋๋ก ๋ณด๋ OSIV ์ ๊ณ ์ฐฐ (2) | 2024.01.30 |
| QueryDSL transform() ์์ ๊ผญ @Transactional ์ ์ฌ์ฉํด์ผ ํ๋ ์ด์ (0) | 2024.01.29 |
