๐Ÿค– Backend/SpringBoot

@DataJpaTest๋ฅผ ์ด์šฉํ•œ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘ ๋ฐœ์ƒํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ์ฟผ๋ฆฌ ์˜ˆ์™ธ ํ•ด๊ฒฐ

sckwon770 2024. 1. 26. 15:23

@DataJpaTest๋ฅผ ์ด์šฉํ•œ Repository ํ…Œ์ŠคํŠธ ์ค‘ ๋ณธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰์—์„œ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋˜ DDL์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. H2 ↔๏ธ MySQL ๋ฌธ๋ฒ• ์ฐจ์ด๋กœ ์ธํ•ด ํ…Œ์ด๋ธ”์ด ์ œ๋Œ€๋กœ ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์—ฌ์„œ application-test.properties ๋ฅผ ๊ณ„์† ์ˆ˜์ •ํ–ˆ์ง€๋งŒ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜๋‹ค. ๋ฌธ๋“, ์ง€๋‚œ ๋ฒˆ์—๋„ @DataJpaTest ํ…Œ์ŠคํŠธ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋ฅผ @AutoConfigureTestDatabase์œผ๋กœ ํ•ด๊ฒฐํ•œ ๊ธฐ์–ต์ด๋‚˜์„œ ์‹œ๋„ํ•˜์˜€๊ณ  ํ•ด๊ฒฐ๋˜์—ˆ๋‹ค. ๊ทธ๋•Œ๋„ ๋””๋ฒ„๊น… ๊ณผ์ • ๊ธฐ๋ก์„ ๋ฏธ๋ค„๋‘๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, ์ด ์ฐธ์— ๋ธ”๋กœ๊น…ํ•˜๊ณ  ์žฅ๊ธฐ ๊ธฐ์–ต์œผ๋กœ ๊ฐ€๋ณด์ž.


๋ฌธ์ œ ๋ถ„์„

์‹œ๋„ํ•  ๋•Œ๋งˆ๋‹ค ๋‹ค์–‘ํ•œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์ธํ„ฐ๋„ท์—๋„ ๋‹ค์–‘ํ•œ ์˜ˆ์™ธ ์ผ€์ด์Šค๊ฐ€ ์žˆ๋Š”๋ฐ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ • ๋ฌธ์ œ์ด๊ฑฐ๋‚˜ DDL ํ˜น์€ SQL ๋ฌธ๋ฒ• ์˜ค๋ฅ˜๋‹ค. ๊ณตํ†ต์ ์œผ๋กœ DataSoruce๋กœ ์ธํ•ด ๋ฐœ์ƒํ•œ ๊ฒƒ์ธ๋ฐ, ๋ณธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ •์ƒ์ ์œผ๋กœ ๋ฌ๋Š”๋ฐ, ์™œ ํ…Œ์ŠคํŠธํ•  ๋•Œ๋งŒ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๊ฑธ๊นŒ? @DataJpaTest๋ฅผ ์ž์„ธํžˆ ์‚ดํŽด๋ณด์ž

  • CommandAcceptanceException: Error executing DDL - ... (this database is empty)
  • JdbcSQLSyntaxErrorException: Syntax error in SQL statement ... expected "identifier"
/**  
 * Annotation for a JPA test that focuses <strong>only</strong> on JPA components.  
 * <p>  
 * Using this annotation will disable full auto-configuration and instead apply only  
 * configuration relevant to JPA tests. * <p>  
 * By default, tests annotated with {@code @DataJpaTest} are transactional and roll back  
 * at the end of each test. They also use an embedded in-memory database (replacing any * explicit or usually auto-configured DataSource). The * {@link AutoConfigureTestDatabase @AutoConfigureTestDatabase} annotation can be used to  
 * override these settings. * <p>  
 * SQL queries are logged by default by setting the {@code spring.jpa.show-sql} property  
 * to {@code true}. This can be disabled using the {@link DataJpaTest#showSql() showSql}  
 * attribute. * <p>  
 * If you are looking to load your full application configuration, but use an embedded  
 * database, you should consider {@link SpringBootTest @SpringBootTest} combined with  
 * {@link AutoConfigureTestDatabase @AutoConfigureTestDatabase} rather than this  
 * annotation. * <p>  
 * When using JUnit 4, this annotation should be used in combination with  
 * {@code @RunWith(SpringRunner.class)}.  
 * * @author Phillip Webb  
 * @author Artsiom Yudovin  
 * @author Scott Frederick  
 * @since 1.4.0  
 * @see AutoConfigureDataJpa  
 * @see AutoConfigureTestDatabase  
 * @see AutoConfigureTestEntityManager  
 * @see AutoConfigureCache  
 */  
@Target(ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Inherited  
@BootstrapWith(DataJpaTestContextBootstrapper.class)  
@ExtendWith(SpringExtension.class)  
@OverrideAutoConfiguration(enabled = false)  
@TypeExcludeFilters(DataJpaTypeExcludeFilter.class)  
@Transactional  
@AutoConfigureCache  
@AutoConfigureDataJpa  
@AutoConfigureTestDatabase  
@AutoConfigureTestEntityManager  
@ImportAutoConfiguration  
public @interface DataJpaTest {
    // ...
}

์ฃผ์„์—๋„ ์ž˜ ์„ค๋ช…๋˜์–ด ์žˆ๋Š” @DataJpaTest ์˜ ํŠน์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • full application context๋ฅผ ๋กœ๋“œํ•˜์ง€ ์•Š๊ณ  JPA ํ…Œ์ŠคํŠธ์™€ ๊ด€๋ จ๋œ configuration๋งŒ ํ™œ์„ฑํ™”ํ•˜๋Š” ํ…Œ์ŠคํŠธ ์–ด๋…ธํ…Œ์ด์…˜
  • Repository ๊ณ„์ธต์— ๋Œ€ํ•œ ๊ฒฉ๋ฆฌ๋œ ํ…Œ์Šฝ ํ™˜๊ฒฝ์„ ์„ค์ •ํ•˜๋Š”๋ฐ ์œ ์šฉ
  • ์ž„๋ฒ ๋””๋“œ ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉ

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ํ•  ๋•Œ๋„ H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ๋ญ”๊ฐ€ ๋‹ค๋ฅธ๊ฑธ๊นŒ? The @AutoConfigureTestDatabase annotation can be used to override these settings. ๋ผ๊ณ  ํ•˜๋‹ˆ, @AutoConfigureTestDatabase ์„ ์‚ดํŽด๋ณด์ž.

@Target({ ElementType.TYPE, ElementType.METHOD })  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Inherited  
@ImportAutoConfiguration  
@PropertyMapping("spring.test.database")  
public @interface AutoConfigureTestDatabase {  

    /**  
     * Determines what type of existing DataSource bean can be replaced.     * @return the type of existing DataSource to replace  
     */    @PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE)  
    Replace replace() default Replace.ANY;  

    /**  
     * The type of connection to be established when {@link #replace() replacing} the  
     * DataSource. By default, will attempt to detect the connection based on the     * classpath.     * @return the type of connection to use  
     */    
     EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE;  

    /**  
     * What the test database can replace.     
     * */    
     enum Replace {  

       /**  
        * Replace the DataSource bean whether it was auto-configured or manually defined.        
        * */       
        ANY,  

       /**  
        * Only replace the DataSource if it was auto-configured.        
        * */       
        AUTO_CONFIGURED,  

       /**  
        * Don't replace the application default DataSource.        
        * */       
        NONE  

    }  

}

@PropertyMapping("spring.test.database")์„ ํ†ตํ•ด ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, application property์—์„œ ๋”ฐ๋กœ ์„ค์ •ํ•œ ๊ฐ’์ด ์—†์œผ๋‹ˆ ๋””ํดํŠธ ์„ค์ •์„ ๋”ฐ๋ฅธ๋‹ค. replace ๊ธฐ๋ณธ ์ „๋žต์€ ANY๋Š” ์ž„๋ฒ ๋””๋“œ ๋ฌด์กฐ๊ฑด ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ DataSource๋กœ ์„ค์ •ํ•˜๊ณ , connection ๊ธฐ๋ณธ ์ „๋žต์€ NONE์ด์ง€๋งŒ EmbeddedDatabaseConnection, TestDatabaseAutoConfiguration ๋“ฑ์„ ํ†ตํ•ด ClassLoader๊ธฐ๋ฐ˜์œผ๋กœ ์ ํ•ฉํ•œ ์ปค๋„ฅ์…˜์„ ์ œ๊ณตํ•œ๋‹ค. ๋‚˜์˜ ๊ฒฝ์šฐ๋Š” H2 ์ปค๋„ฅ์…˜์œผ๋กœ ์„ค์ •๋˜๋Š” ๊ฒƒ์„ ๋กœ๊ทธ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 


์›์ธ ํŒŒ์•…

์–ด? ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ™˜๊ฒฝ๊ณผ ๋™์ผํ•œ H2์ธ๋ฐ ์™œ ์•ˆ๋˜์ง€? ๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค๋ฉด, ๋‹น์‹ ์€ ๋””๋ฒ„๊น… ๊ตฌ๋ ํ……์ด์— ๋น ์ง€๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.... ์ด ์„ค์ •์€ EmbeddedDatabaseConnection์— ์„ค์ •๋œ ๊ธฐ๋ณธ H2 url์— ๋žœ๋คํ•œ ์œ ๋‹ˆํฌ UUID๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„์œผ๋กœ ์ž๋™ ์ƒ์„ฑ๋œ ์ž„๋ฒ ๋””๋“œ ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ธ๋ฐ, ;MODE=MYSQL;์ด ์—†๋‹ค!! ์ดˆ๊ธฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ธํŒ… ์ดํ›„ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„ ๊นŒ๋จน์—ˆ์„ ํ…๋ฐ, H2์™€ MySQL์€ ์œ ์‚ฌํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ๋ฌธ๋ฒ•์ด ์กด์žฌ(ํŠนํžˆ DDL)ํ•˜๊ธฐ ๋•Œ๋ฌธ์— MySQL ๋ชจ๋“œ๋กœ DataSource๋ฅผ ์„ค์ •ํ•ด์•ผ๋งŒ ํ–ˆ๋‹ค.

public enum EmbeddedDatabaseConnection {  

    /**  
     * No Connection.     
     * */    
     NONE(null),  

    /**  
     * H2 Database Connection.     
     * */    
     H2("jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"),  

    /**  
     * Derby Database Connection.     
     * */    
     DERBY("jdbc:derby:memory:%s;create=true"),  

    /**  
     * HSQL Database Connection.     * @since 2.4.0  
     */    
     HSQLDB("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:%s");

    // ...
}

ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ

  1. spring.jpa.properties.hibernate.dialect ์„ ๊ธฐ๋ณธ ๊ฐ’ ํ˜น์€ H2Dialect๋กœ ์„ค์ •ํ•œ๋‹ค.
  2. AutoConfigureTestDatabase ์˜ Replace ์ „๋žต์„ NONE์œผ๋กœ ์„ค์ •ํ•œ๋‹ค.

์ฒ˜์Œ๋ถ€ํ„ฐ ๋กœ์ปฌ์—์„œ H2๋กœ ์“ด ์‚ฌ๋žŒ์ด๋ผ๋ฉด 1๋ฒˆ์„ ํƒํ•˜๋ฉด ๋˜์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ๋งž์ถ”๊ธฐ ์œ„ํ•ด MySQL ๋ชจ๋“œ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ์‚ฌ๋žŒ๋„ ๋งŽ์„ ๊ฒƒ์ด๋‹ค. ํŠนํžˆ ๋‚˜๋Š” ๋‚ ์งœ์™€ ๊ด€๋ จ๋œ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, H2์™€ MySQL์—์„œ ์ง€์›ํ•˜๋Š” ๋‚ ์งœ ํ•จ์ˆ˜๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— 1๋ฒˆ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. 1๋ฒˆ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด Replace ์ „๋žต์„ NONE์œผ๋กœ ์„ค์ •ํ•˜๋ฉด, properties์—์„œ ์„ค์ •ํ•œ DataSource๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

@DataJpaTest  
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ActiveProfiles("test")  
class MemberPostRepositoryCustomTest {
    // ...
}

์˜ˆ์™ธ๋ช…๊ณผ Caused by ์ ˆ์„ ํ†ตํ•ด์„œ๋Š” ์›์ธ์„ ์•Œ ์ˆ˜ ์—†๋Š” ๋ฌธ์ œ์˜€๋‹ค. ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ œ๋ฅผ ์ถ”์ธกํ•˜๊ณ  ๊ตฌ๊ธ€๋ง๊ณผ ํ”„๋ ˆ์ž„์›Œํฌ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•จ์œผ๋กœ์จ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋Š”๋ฐ, ์ด๊ฒŒ ์ง„์งœ ์ด์ƒ์ ์ธ ๋””๋ฒ„๊น…๊ฐ™์•„์„œ ๋ฟŒ๋“ฏํ–ˆ๋‹ค. ๋ธ”๋กœ๊น…์—๋„ ๊ฝค ๋งŽ์€ ์‹œ๊ฐ„์ด ๋“ค์—ˆ๋Š”๋ฐ, ๊ฒฐ๊ตญ์€ ๊ธฐ๋ก์ด ๋‹ค ๋‚จ๋Š” ๊ฒƒ ๊ฐ™์œผ๋‹ˆ ์—ด์‹ฌํ•˜ ํ•ด๋ด์•ผ๊ฒ ๋‹ค.