๐ง ์ธ์ํ ์คํธ ๋ฐ์ดํฐ ์ด๊ธฐํ
์ธ์ํ ์คํธ ํน์ ํตํฉํ ์คํธ๋ฅผ ์์ฑํ ๋์๋ ํ ์คํธ๋ฅผ ๊ฒฉ๋ฆฌํ๊ธฐ ์ํด ๋ค์ํ ๋ฐฉ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ด์ ๊ด๋ จํ ์ฌ๋ฌ ๋ฐฉ๋ฒ์ ๋ํด์๋ ์ธ์ํ ์คํธ์์ ํ ์คํธ ๊ฒฉ๋ฆฌํ๊ธฐ ๊ธ์ ์์ธํ ๋ํ๋ ์์ต๋๋ค.
์ ๋ ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ๋ค ์ค @Sql ์ ๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ truncate ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์ฃผ๋ก ์ฌ์ฉํ์๋๋ฐ์, ์ด ๋ฐฉ๋ฒ์ truncate.sql ํ์ผ์ ์ฝ๋๊ฐ ์์กด์ ์ด๊ฒ ๋๋ฉฐ, ํ ์ด๋ธ์ด ์๋ก ์ถ๊ฐ๋๊ฑฐ๋ ์ญ์ ๋๋ ๊ฒฝ์ฐ truncate.sql ํ์ผ๋ ๊ณ์ํด์ ์์ ํด์ฃผ์ด์ผ ํ๋ค๋ ๋จ์ ์ ๋๊ผ์ต๋๋ค.
๊ทธ๋ฌ๋ ์ค BeforeEachCallback๋ผ๋ ๊ฒ์ ๋ํด ์๊ฒ๋์๊ณ , ์ด๋ฅผ ์ฌ์ฉํ์ฌ truncate.sql ํ์ผ ์์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ด๊ธฐํ๋ฅผ ์๋ํ ์์ผฐ๋๋ฐ์, ํด๋น ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ณด๊ฒ ์ต๋๋ค.
๐ง truncate๋ก ๋ชจ๋ ํ ์ด๋ธ ์ด๊ธฐํํ๊ธฐ
์ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ ์ด๋ธ์ ์กฐํํ ๋ค, ํด๋น ํ ์ด๋ธ๋ค์ truncate ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.
์์ฑ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
package com.vestie.vestie.common;
import jakarta.annotation.PostConstruct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
@Profile("test")
public class DataCleaner {
private static final String FOREIGN_KEY_CHECK_FORMAT = "SET FOREIGN_KEY_CHECKS %d";
private static final String TRUNCATE_FORMAT = "TRUNCATE TABLE %s";
private final List<String> tableNames = new ArrayList<>();
@PersistenceContext
private EntityManager entityManager;
@SuppressWarnings("unchecked")
@PostConstruct
public void findDatabaseTableNames() {
List<Object[]> tableInfos = entityManager.createNativeQuery("SHOW TABLES").getResultList();
for (Object[] tableInfo : tableInfos) {
String tableName = (String) tableInfo[0];
tableNames.add(tableName);
}
}
@Transactional
public void clear() {
entityManager.clear();
truncate();
}
private void truncate() {
entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, 0)).executeUpdate();
for (String tableName : tableNames) {
entityManager.createNativeQuery(String.format(TRUNCATE_FORMAT, tableName)).executeUpdate();
}
entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, 1)).executeUpdate();
}
}
๐ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์กด์ฌํ๋ ๋ชจ๋ ํ ์ด๋ธ์ ์ด๋ฆ ๊ฐ์ ธ์ค๊ธฐ
๋จผ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์กด์ฌํ๋ ํ ์ด๋ธ์ ์ด๋ฆ์ ๊ฐ์ ธ์ค๋ findDatabaseTableNames() ์ฝ๋๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
@PostConstruct
@SuppressWarnings("unchecked")
public void findDatabaseTableNames() {
List<Object[]> tableInfos = entityManager.createNativeQuery("SHOW TABLES").getResultList();
for (Object[] tableInfo : tableInfos) {
String tableName = (String) tableInfo[0];
tableNames.add(tableName);
}
}
SHOW TABLE๋ผ๋ native query๋ฅผ ์ํํ ํ, ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์์ต๋๋ค.
H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ด๋ TABLE_NAME๊ณผ TABLE_SCHEMA๊ฐ Object ๋ฐฐ์ด์ ๋ค์ด๊ฐ ํํ๋ก ์กฐํ๋ฉ๋๋ค.
๋ฐ๋ผ์ ์ด๋ค์ ์ํํ์ฌ ๋ฐฐ์ด์ 0๋ฒ์งธ ์ธ๋ฑ์ค์ ์๋ ๊ฐ๋ค์ ์ถ์ถํ๋ฉด, ๊ทธ๊ฒ์ด ํ ์ด๋ธ์ ์ด๋ฆ์ด ๋ฉ๋๋ค.
๐ณ ๊ฐ๊ฐ์ ํ ์ด๋ธ๋ค์ ๋ํด truncate ์ฟผ๋ฆฌ ์ํํ๊ธฐ
private static final String FOREIGN_KEY_CHECK_FORMAT = "SET FOREIGN_KEY_CHECKS %d";
private static final String TRUNCATE_FORMAT = "TRUNCATE TABLE %s";
private void truncate() {
entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, 0)).executeUpdate();
for (String tableName : tableNames) {
entityManager.createNativeQuery(String.format(TRUNCATE_FORMAT, tableName)).executeUpdate();
}
entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, 1)).executeUpdate();
}
์ด ๋ถ๋ถ์ truncate.sql์ ์์ฑ๋ ์ฝ๋๋ฅผ ๊ทธ๋๋ก ์ฎ๊ธด ๊ฒ์ ๋๋ค.
๐ณ ์ฌ์ฉ ์ ๋ฌธ์ ์
์ด์ DataCleaner๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๋ฉ๋๋ค.
@SpringBootTest(webEnvironment = RANDOM_PORT)
class ์ธ์ํ
์คํธ {
@Autowired
private DataCleaner cleaner;
@BeforeEach
void setup() {
RestAssured.port = port;
cleaner.clear();
}
}
๊ทธ๋ฌ๋ ํ์ํ ํ ์คํธ ํด๋์ค๋ง๋ค ์์กด๊ด๊ณ๋ฅผ ์ฃผ์ ๋ฐ๊ณ , @BeforeEach๋ฅผ ์์ฑํ๋ ๊ฒ์ ๊ท์ฐฎ์ ์ ์์ต๋๋ค.
์ธ์ํ ์คํธ์๋ง ์ฌ์ฉ๋๋ค๋ฉด ์์์ ํตํด ๊ท์ฐฎ์์ ํด๊ฒฐํ ์ ์๊ฒ ์ง๋ง, ํตํฉ ํ ์คํธ ๋ฑ์์๋ ํ์ํ ์ ์๊ธฐ ๋๋ฌธ์, ์ด๋ค์ ๊ณตํต์ผ๋ก ์๋ํํ๋ ๋ฐฉ๋ฒ์ด ์์ผ๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
์ด๋ฅผ ์ํด BeforEachCallback์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๐ง BeforeEachCallback
Junit5์์ ์ ๊ณตํด์ฃผ๋ ์ธํฐํ์ด์ค๋ก @BeforeEach ์คํ ์ ์ ์คํ๋๋ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
์ด ์ธ์๋ ๋ค์๊ณผ ๊ฐ์ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
- BeforeAllCallback - @BeforeAll ์คํ ์ ์ ์คํ๋ฉ๋๋ค. (๊ฐ์ฅ ๋จผ์ ์คํ๋ฉ๋๋ค.)
- BeforeEachCallback - @BeforeEach ์คํ ์ ์ ์คํ๋ฉ๋๋ค.
- BeforeTestExecutionCallback - ๊ฐ ํ ์คํธ๊ฐ ์คํ๋๊ธฐ ์ง์ ์ ์คํ๋ฉ๋๋ค. (@BeforeEach ํ์ ์คํ๋ฉ๋๋ค.)
- AfterTestExecutionCallback : ๊ฐ ํ ์คํธ๊ฐ ์ข ๋ฃ๋ ํ ์คํ๋ฉ๋๋ค. (@AfterEach์ ์ ์คํ๋ฉ๋๋ค.)
- AfterEachCallback : @AfterEach ์คํ ์ดํ์ ์คํ๋ฉ๋๋ค.
- AfterAllCallback : @AfterAll ์คํ ์ดํ์ ์คํ๋ฉ๋๋ค. (๊ฐ์ฅ ๋์ค์ ์คํ๋ฉ๋๋ค.)
๐ง truncate ์๋ํํ๊ธฐ
public class DataClearExtension implements BeforeEachCallback {
@Override
public void beforeEach(ExtensionContext context) throws Exception {
DataCleaner dataCleaner = getDataCleaner(context);
dataCleaner.clear();
}
private DataCleaner getDataCleaner(ExtensionContext extensionContext) {
return SpringExtension.getApplicationContext(extensionContext)
.getBean(DataCleaner.class);
}
}
์์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ์ฌ @BeforeEach ์คํ ์ ์ ํ ์ด๋ธ๋ค์ truncateํ๋๋ก ๋ง๋ค์ด์ค๋๋ค.
์ด์ ์ด๋ฅผ ๋ค์๊ณผ ๊ฐ์ด @ExtendWith์ ํตํด ์ฌ์ฉํ ์ ์์ต๋๋ค.
@ExtendWith(DataClearExtension.class) // DB ์ด๊ธฐํ ์๋ํ
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ์ธ์ํ
์คํธ {
@LocalServerPort
private int port;
@BeforeEach
void setup() {
RestAssured.port = port;
}
}
'๐๏ธ Spring > ๊ธฐ๋ณธ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Repository์ DAO์ ์ฐจ์ด์ ์ ๋ฌด์์ผ๊น (16) | 2023.04.24 |
---|---|
[Spring] @TransactionalEventListener ํธ์ถ ์ ๋ฐ์ํ๋ ๋ฌธ์ (2) | 2023.01.05 |
[Spring] @ConfigurationProperties๋ฅผ ํตํด ํ๋กํผํฐ ์ค์ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ (@ConstructorBinding) (0) | 2022.08.05 |
[Spring] ๋ฑ๋ก๋ ์คํ๋ง ๋น์ ์กฐํํ๋ ๋ฐฉ๋ฒ (0) | 2022.07.19 |
[Spring] ์ฑ๊ธํค ํจํด์ ์ฃผ์์ (0) | 2021.12.28 |