ํ…Œ์ŠคํŠธ์—์„œ @Sql ๋กœ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด๊ฐ€์ง€ ์•Š์•˜๋˜ ์ด์œ  (with. custom TestExecutionListener)

2024. 1. 24. 13:06ยท๐Ÿ“‚ backend/spring

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) ๐Ÿซ  = ๋‹น์—ฐํžˆ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค!)

์ •๋ง bloody hell...

 

 

[3] ํ•ด๊ฒฐ : custom TestExecutionListener ์ˆœ์„œ ์ž…๋ ฅ

TRUNCATE ๋กœ์ง ์ดํ›„์— @Sql ์‹คํ–‰๋˜๊ธฐ ์œ„ํ•ด Order ๊ฐ’์„ SqlScriptsTestExecutionListener ์˜ ๊ฐ’๋ณด๋‹ค ๋‚ฎ๊ฒŒ ์„ค์ •ํ–ˆ๋‹ค. ( TRUNCATE ์‹คํ–‰ → @Sql ์‹คํ–‰)

 

 

์ •์ƒ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ฑ๋ก๋˜์—ˆ๋‹ค. ๐Ÿ‘

 

์˜ค๋Š˜์˜ ๊ตํ›ˆ

  1. custom TestExecutionListener ๋งŒ๋“ค ๋•Œ๋Š” ๊ธฐ์กด์˜ TestExecutionListener ์™€์˜ ์ˆœ์„œ๋ฅผ ๋น„๊ตํ•ด์„œ ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜์ž.

 

Reference

  • https://www.baeldung.com/spring-testexecutionlistener
  • https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/bootstrapping.html
  • https://mangkyu.tistory.com/264

'๐Ÿ“‚ 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
'๐Ÿ“‚ backend/spring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • JPA ์— UUID version 7 ์ ์šฉํ•˜๊ธฐ
  • [Spring] Spring AOP ๋™์ž‘ ์•Œ์•„๋ณด๊ธฐ
  • [JPA] ์ฝ”๋“œ๋กœ ๋ณด๋Š” OSIV ์™€ ๊ณ ์ฐฐ
  • QueryDSL transform() ์—์„œ ๊ผญ @Transactional ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ 
cooper25
cooper25
  • cooper25
    dev cooper
    cooper25
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (84)
      • ๐Ÿ“‚ backend (34)
        • spring (19)
        • architecture (10)
        • test (5)
      • ๐Ÿ“‚ computer-science (5)
      • ๐Ÿ“‚ programming-language (12)
        • java (12)
      • ๐Ÿ“‚ infra (9)
        • mysql (4)
        • redis (2)
        • message-queue (3)
      • ๐Ÿ“‚ cloud (2)
        • aws (2)
      • ๐Ÿ“‚ frontend (1)
        • react (0)
      • ๐Ÿ“‚ education & lecture (16)
        • ์ธํ”„๋ผ ๊ณต๋ฐฉ (11)
        • ํ•ญํ”Œ ๋ฐฑ์—”๋“œ 7๊ธฐ (5)
      • ๐Ÿ“‚ ai (1)
        • claude (1)
      • ๐Ÿ“‚ etc (2)
        • ํšŒ๊ณ  (1)
        • ์ปจํผ๋Ÿฐ์Šค (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค๊ณ„
    ์Šคํ”„๋ง์บ ํ”„ ํ›„๊ธฐ
    gof
    ํšŒ๊ณ 
    ๋™์‹œ์„ฑ
    Redisson
    spring AOP
    ํ•ญํ•ดํ”Œ๋Ÿฌ์Šค
    JPA
    ๊ฐ€์ƒ ๋ฉด์ ‘ ์‚ฌ๋ก€๋กœ ๋ฐฐ์šฐ๋Š” ๋Œ€๊ทœ๋ชจ ์„ค๊ณ„
    nGrinder
    UUID
    kafka
    mysql
    spring camp 2025
    react
    spring-batch
    spring
    AWS
    ์ธํ”„๋ผ๊ณต๋ฐฉ
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.6
cooper25
ํ…Œ์ŠคํŠธ์—์„œ @Sql ๋กœ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด๊ฐ€์ง€ ์•Š์•˜๋˜ ์ด์œ  (with. custom TestExecutionListener)
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”