๋ฐฐ๊ฒฝ
ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ 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 ๋ฅผ ์ฌ์ฉํ ์ ์๋ ๊ฒ์
๋๋ค.