Search
Duplicate
๐Ÿงจ

h2 ๊ฐ€ test DB ๋กœ ์„ ์ •๋˜๋Š” ์ด์œ  in DataJpaTest

์ƒ์„ฑ์ผ
2022/07/20
ํƒœ๊ทธ
test
JPA

๋ฐฐ๊ฒฝ

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ DB ๊ด€๋ จ ํ…Œ์ŠคํŠธ๋ฅผ DataJpaTest ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ์ € ๋‹จ์ˆœํ•œ ์ด์œ ๋กœ ๋ ˆํฌ์ง€ํ† ๋ฆฌ ํ…Œ์ŠคํŠธ์—์„œ SpringBootTest ๋กœ ๋ชจ๋“  ๋นˆ์„ ๋„์šธ ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ด ์ด์œ ์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค ์ œ์ด์Šจ์˜ JPA Hands-on ์ฒซ ๋ฒˆ์งธ ๊ฐ•์˜๋ฅผ ๋๋‚ด๊ณ  ์ ์‹ฌ์„ ๋จน๊ณ  ๋‚˜์„œ ์„œ์„ฑ๊ฑฐ๋ฆฌ๊ณ  ์žˆ๋Š”๋ฐ ์—˜๋ฆฌ๊ฐ€ ํ…Œ์ŠคํŠธ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค๊ณ  ํ•ด์„œ ๊ตฌ๊ฒฝํ•˜๋Ÿฌ ๊ฐ”์Šต๋‹ˆ๋‹ค. ๋Œ€์ถฉ ์—๋Ÿฌ ๋‚ด์šฉ์€ h2 ๊ด€๋ จ์ด์—ˆ๋Š”๋ฐ ์ฝ”๋“œ๊ฐ€ ์ œ๊ฐ€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ž‘ ๋‹ค๋ฅผ ๊ฒƒ์ด ์—†์—ˆ๋Š”๋ฐ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์•„๋ฌด๋ž˜๋„ ๊ธฐ๋ณธ ์„ค์ • ๊ฐ’์— ์ด์œ ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ณ , @DataJpaTest ๋ฅผ ํƒ์ƒ‰ํ•ด๋ณด๊ธฐ๋กœ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
@DataJpaTest ์—๋Š” ๋งŽ์€ ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ๋Š”๋ฐ ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๊ทธ ์ค‘ @AutoConfigureTestDatabase ์–ด๋…ธํ…Œ์ด์…˜์„ ํƒ์ƒ‰ํ•˜๊ณ  ๋‚˜์˜จ ๊ฒฐ๊ณผ๋ฅผ ๋ถ„์„ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @BootstrapWith(DataJpaTestContextBootstrapper.class) @ExtendWith(SpringExtension.class) @OverrideAutoConfiguration(enabled = false) @TypeExcludeFilters(DataJpaTypeExcludeFilter.class) @Transactional @AutoConfigureCache @AutoConfigureDataJpa @AutoConfigureTestDatabase// -> this annotation! @AutoConfigureTestEntityManager @ImportAutoConfiguration public @interface DataJpaTest { }
Java
๋ณต์‚ฌ

AutoConfigureTestDatabase

AutoConfigureTestDatabase ์–ด๋…ธํ…Œ์ด์…˜์€ ํ…Œ์ŠคํŠธ DB ๊ด€๋ จ ์„ค์ • ์–ด๋…ธํ…Œ์ด์…˜์ž…๋‹ˆ๋‹ค.
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @ImportAutoConfiguration @PropertyMapping("spring.test.database")// --1 public @interface AutoConfigureTestDatabase { @PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE) Replace replace() default Replace.ANY;// --2 EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE;// --3 enum Replace { ANY, AUTO_CONFIGURED, NONE } }
Java
๋ณต์‚ฌ
1. PropertyMapping
yaml ํŒŒ์ผ์ด๋‚˜ properties ํŒŒ์ผ ๋“ฑ๊ณผ ๊ฐ™์€ ์„ค์ • ํŒŒ์ผ์˜ placeholder ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ํ•ด๋‹น PropertyMapping์€ ์„ค์ • ํŒŒ์ผ์˜ spring.test.database ์˜ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ, ๊ธฐ๋ณธ์€ replace.any ์ž…๋‹ˆ๋‹ค(์•„๋ž˜ ๊ฐ’).
2. Replace
๋‚ด์žฅ๋œ DB ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ธ์ง€ ์•„๋‹Œ์ง€๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ ANY ์ž…๋‹ˆ๋‹ค(๋Œ€์ถฉ ๋‚ด์žฅ DB ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๋œป).
3. EmbeddedDatabaseConnection
์œ„์˜ Replace ์—์„œ ๋Œ€์ฒดํ•  ๋‚ด์žฅ DB ์˜ ํƒ€์ž…์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. H2, DERBY ๋“ฑ์˜ ๋‚ด์žฅ DB ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

TestDatabaseAutoConfiguration

TestDatabaseAutoConfiguration ์€ ํ…Œ์ŠคํŠธ DB ๊ด€๋ จ ์„ค์ • ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.
@Configuration(proxyBeanMethods = false) @AutoConfigureBefore(DataSourceAutoConfiguration.class) public class TestDatabaseAutoConfiguration { @Bean @ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "AUTO_CONFIGURED") @ConditionalOnMissingBean public DataSource dataSource(Environment environment) { return new EmbeddedDataSourceFactory(environment).getEmbeddedDatabase(); } @Bean @ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "ANY", matchIfMissing = true) public static EmbeddedDataSourceBeanFactoryPostProcessor embeddedDataSourceBeanFactoryPostProcessor() { return new EmbeddedDataSourceBeanFactoryPostProcessor(); } // ... }
Java
๋ณต์‚ฌ
embeddedDataSourceBeanFactoryPostProcessor() ๊ฐ€ ์„ค์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋นˆ์œผ๋กœ ๋“ฑ๋ก์ด ๋˜๋Š”๋ฐ, ์•ž์„œ ์–ธ๊ธ‰ํ•œ AutoConfigureTestDatabase ์˜ Replace ๋””ํดํŠธ ๊ฐ’์ด ANY ์ด๊ธฐ๋•Œ๋ฌธ์— EmbeddedDataSourceBeanFactoryPostProcessor๊ฐ€ ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜๊ณ  ๋‚ด์žฅ DB์˜ ๊ธธ์„ ๊ฑท์Šต๋‹ˆ๋‹ค. ์ดํ›„ EmbeddedDataSourceBeanFactoryPostProcessor ์˜ process() ์™€ EmbeddedDataSourceFactoryBean ์„ ๊ฑฐ์ณ EmbeddedDataSourceFactory ์˜ getEmbeddedDatabase() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋‚ด์žฅ DB ๊ฐ€ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค. EmbeddedDataSourceBeanFactoryPostProcessor,ย EmbeddedDataSourceFactoryBean,ย EmbeddedDataSourceFactory ๋ชจ๋‘ TestDatabaseAutoConfiguration ์˜ ๋‚ด๋ถ€ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.

EmbeddedDataSourceFactory

@Configuration(proxyBeanMethods = false) @AutoConfigureBefore(DataSourceAutoConfiguration.class) public class TestDatabaseAutoConfiguration { static class EmbeddedDataSourceFactory { EmbeddedDatabase getEmbeddedDatabase() { EmbeddedDatabaseConnection connection = this.environment.getProperty("spring.test.database.connection", EmbeddedDatabaseConnection.class, EmbeddedDatabaseConnection.NONE); if (EmbeddedDatabaseConnection.NONE.equals(connection)) {// -- 1 connection = EmbeddedDatabaseConnection.get(getClass().getClassLoader()); } Assert.state(connection != EmbeddedDatabaseConnection.NONE, "Failed to replace DataSource with an embedded database for tests. If " + "you want an embedded database please put a supported one " + "on the classpath or tune the replace attribute of @AutoConfigureTestDatabase."); return new EmbeddedDatabaseBuilder().generateUniqueName(true).setType(connection.getType()).build(); } }
Java
๋ณต์‚ฌ
์œ„์˜ย AutoConfigureTestDatabase ์˜ connection ์—์„œย EmbeddedDatabaseConnection.NONE ๊ฐ€ ๊ธฐ๋ณธ๊ฐ’์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ 1๋ฒˆ ๋ถ€๋ถ„์—์„œ true ๊ฐ€ ๋˜์–ด if ๋‚ด๋ถ€๋กœ ๋“ค์–ด๊ฐ€ย EmbeddedDatabaseConnection ์˜ get() ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

EmbeddedDatabaseConnection

public static EmbeddedDatabaseConnection get(ClassLoader classLoader) { for (EmbeddedDatabaseConnection candidate : EmbeddedDatabaseConnection.values()) { if (candidate != NONE && ClassUtils.isPresent(candidate.getDriverClassName(), classLoader)) { return candidate; } } return NONE; }
Java
๋ณต์‚ฌ
ํ•ด๋‹น EmbeddedDatabaseConnection ์€ enum ํƒ€์ž…์ด๊ณ  values ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ์•Œ๋งž๋Š”ย EmbeddedDatabaseConnection ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
public enum EmbeddedDatabaseConnection { NONE(null, null, null, (url) -> false), H2(EmbeddedDatabaseType.H2, DatabaseDriver.H2.getDriverClassName(), "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", (url) -> url.contains(":h2:mem")), DERBY(EmbeddedDatabaseType.DERBY, DatabaseDriver.DERBY.getDriverClassName(), "jdbc:derby:memory:%s;create=true", (url) -> true), HSQLDB(EmbeddedDatabaseType.HSQL, DatabaseDriver.HSQLDB.getDriverClassName(), "org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:%s", (url) -> url.contains(":hsqldb:mem:")); //... }
Java
๋ณต์‚ฌ
์šฐ๋ฆฌ๋Š” h2 ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์„ค์ • ํŒŒ์ผ์— ์ถ”๊ฐ€๊ฐ€ ๋˜์–ด ์žˆ๊ณ , ์ด ๊ธฐ๋ณธ ์„ค์ •๋•Œ๋ฌธ์— ์•ž์—์„œ values๋ฅผ ์ˆœํšŒํ•  ๋•Œ h2 ๊ฐ€ ์„ ์ •์ด ๋˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” DataJpaTest ๋งŒ ๋ถ™์ด๋ฉด h2 ๋‚ด์žฅ DB ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.