- ์ด๋ฒ 4์ฅ์์๋ ํ ์คํธ๊ฐ ์ํ๋๋ ๋์ ๋์ ์ผ๋ก ์ปจํ ์ด๋๋ฅผ ์์ฑํ๊ณ ์ข ๋ฃํ๋ ์์ ์ ์ํํ๋๋ก ํ๊ฒ ์ต๋๋ค.
- ํ ์คํธ ์์ : ํ ์คํธ๋ฅผ ์์ํฉ๋๋ค.
- ์ปจํ ์ด๋ ์์ฑ : testcontainers๊ฐ Docker๋ฅผ ์ด์ฉํด ํ ์คํธ ํ์ํ ์ปจํ ์ด๋๋ค(MySQL, Redis ๋ฑ..)์ ์์ฑํฉ๋๋ค.
- ๋์ ํ๋กํผํฐ ์ฃผ์ : ์์ฑํ ์ปจํ ์ด๋์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์คํ๋ง ํ๋กํผํฐ ๋์ ์ผ๋ก ์ฃผ์ (overwrite)ํฉ๋๋ค.
- ํ ์คํธ ์ํ : ์์ฑํ ์ปจํ ์ด๋๋ฅผ ํ์ฉํ์ฌ ํ ์คํธ๋ฅผ ์ํํฉ๋๋ค.
- ํ ์คํธ ์ข ๋ฃ: ํ ์คํธ ์ข ๋ฃ์ ๋์์ ์์ฑํ๋ ์ปจํ ์ด๋๋ค์ ์ ๋ฆฌํฉ๋๋ค.
1. testcontainers ์ฌ์ฉํ๊ธฐ ์ํด ์ค์ ๋ฐ ๊ฐ๋จํ DB TEST
1) build.gradle
...
testImplementation "org.testcontainers:testcontainers:1.19.0" // testcontainers
}
2) infra/test/docker-compose.yaml
# infra/test/docker-compose.yaml
version: "3.8"
services:
local-db:
image: mysql:8
environment:
MYSQL_DATABASE: score
MYSQL_ROOT_PASSWORD: password
ports:
- 3306
local-db-migrate:
image: flyway/flyway:7
restart: always
environment:
- FLYWAY_DB_URL=jdbc:mysql://local-db/score
- FLYWAY_DB_USER=root
- FLYWAY_DB_PASSWORD=password
command: migrate
volumes:
- ../../db/flyway.conf:/flyway/conf/flyway.conf
- ../../db/migration:/flyway/sql
3) IntegrationTest.class
- ์์น : com.pcy.dayonetest (test)
@Ignore
@Transactional
@SpringBootTest
@ContextConfiguration(initializers = IntegrationTest.IntegrationTestInitializer.class)
public class IntegrationTest {
static DockerComposeContainer rdbms;
static {
rdbms = new DockerComposeContainer(new File("infra/test/docker-compose.yaml"))
.withExposedService(
"local-db",
3306,
Wait.forLogMessage(".*ready for connections.*", 1)
.withStartupTimeout(Duration.ofSeconds(300))
)
.withExposedService(
"local-db-migrate",
0,
Wait.forLogMessage("(.*Successfully applied.*)|(.*Successfully validated.*)", 1)
.withStartupTimeout(Duration.ofSeconds(300))
);
rdbms.start();
}
static class IntegrationTestInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
Map<String, String> properties = new HashMap<>();
String rdbmsHost = rdbms.getServiceHost("local-db", 3306);
Integer rdbmsPort = rdbms.getServicePort("local-db", 3306);
properties.put("spring.datasource.url", "jdbc:mysql://" + rdbmsHost + ":" + rdbmsPort + "/score");
TestPropertyValues.of(properties).applyTo(applicationContext);
}
}
}
- Docker Compose๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ๊ฒฝ์ ์ค์ ํ๊ณ , ํ ์คํธ ์ค์ ํด๋น ํ๊ฒฝ์ ์ฌ์ฉํ ์ ์๋๋ก ๊ตฌ์ฑํฉ๋๋ค.
์๋์์ ๊ฐ ๋ถ๋ถ์ ์์ธํ ์ค๋ช ํ๊ฒ ์ต๋๋ค.
[1] ์ด๋ ธํ ์ด์
• @Ignore: ์ด ํ
์คํธ ํด๋์ค๋ฅผ ๋ฌด์ํ๋๋ก ์ง์ ํฉ๋๋ค. ์ค์ ํ
์คํธ๋ฅผ ์คํํ์ง ์๋๋ก ํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
• @Transactional: ํ
์คํธ ๋ฉ์๋๊ฐ ํธ๋์ญ์
๋ด์์ ์คํ๋๋๋ก ํ์ฌ, ํ
์คํธ๊ฐ ๋๋ ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํ๋ฅผ ๋กค๋ฐฑํฉ๋๋ค.
• @SpringBootTest: Spring Boot ์ ํ๋ฆฌ์ผ์ด์
์ปจํ
์คํธ๋ฅผ ๋ก๋ํ์ฌ ํตํฉ ํ
์คํธ๋ฅผ ์ํํฉ๋๋ค.
• @ContextConfiguration: initializers ์์ฑ์ ์ฌ์ฉํ์ฌ ํ
์คํธ ์ปจํ
์คํธ ์ด๊ธฐํ๋ฅผ ์ค์ ํฉ๋๋ค. ์ฌ๊ธฐ์ IntegrationTestInitializer ํด๋์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค.
[2] Docker Compose ์ค์
• DockerComposeContainer: Testcontainers ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ Docker Compose ํ๊ฒฝ์ ์ค์ ํฉ๋๋ค.
• new File("infra/test/docker-compose.yaml"): docker-compose.yaml ํ์ผ์ ๊ฒฝ๋ก๋ฅผ ์ง์ ํฉ๋๋ค.
• withExposedService: Docker Compose์์ ๋ ธ์ถํ ์๋น์ค๋ฅผ ์ค์ ํฉ๋๋ค.
- local-db: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋น์ค.
- 3306: ๋ฐ์ดํฐ๋ฒ ์ด์ค ํฌํธ.
- Wait.forLogMessage: ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ๊ธฐ๋ค๋ ค์ ์๋น์ค๊ฐ ์ค๋น๋์๋์ง ํ์ธํฉ๋๋ค.
- withStartupTimeout: ์๋น์ค๊ฐ ์ค๋น๋ ๋๊น์ง์ ํ์์์์ ์ค์ ํฉ๋๋ค.
- local-db-migrate: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์ ์๋น์ค.
- 0: ์์์ ํฌํธ ์ฌ์ฉ.
• rdbms.start(): Docker Compose ์ปจํ ์ด๋๋ฅผ ์์ํฉ๋๋ค.
[3] ์ปจํ ์คํธ ์ด๊ธฐํ
• IntegrationTestInitializer: ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ์ด๊ธฐํํ๋ ํด๋์ค์ ๋๋ค.
• initialize: ์ปจํ ์คํธ ์ด๊ธฐํ ๋ฉ์๋์ ๋๋ค.
• rdbmsHost: Docker Compose์์ ์ค์ ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํธ์คํธ ์ฃผ์๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
• rdbmsPort: Docker Compose์์ ์ค์ ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํฌํธ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
• properties.put: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ URL์ ์ค์ ํฉ๋๋ค.
• TestPropertyValues.of(properties).applyTo(applicationContext): ์ค์ ํ ํ๋กํผํฐ ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ์ ์ ์ฉํฉ๋๋ค.
**
์ด ์ฝ๋๋ Docker Compose๋ฅผ ์ฌ์ฉํ์ฌ ํตํฉ ํ ์คํธ๋ฅผ ์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ๊ฒฝ์ ์ค์ ํ๊ณ , Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ ์ด๊ธฐํ ์ ํด๋น ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ๊ฒฝ์ ์ฌ์ฉํ ์ ์๋๋ก ๊ตฌ์ฑํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฌํ ํ๊ฒฝ์์ ํตํฉ ํ ์คํธ๋ฅผ ์ํํ ์ ์์ต๋๋ค.
**
4) DayonetestApplicationTests.class
- ์์น : com.pcy.dayonetest (test)
[1] ์์์ฒ๋ฆฌ
@SpringBootTest
class DayonetestApplicationTests extends IntegrationTest {
@Test
void contextLoads() {
}
}
- IntegrationTest ์์๋ฐ์์ ์คํ์ ํ๋ฉด ์๋์ ๊ฐ์ด TEST ์ปจํ ์ด๋๊ฐ ์ํ๋๋ ๊ฒ์ ํ์ธ ํ ์ ์์ต๋๋ค(ํ ์คํธ ์ํ์ค์๋ง)
[2] ์ค์ ๋ก MySQL์ ์ ๋ ฅ ๋ฐ ์ ๋ ฅ ๋ฐ์ดํฐ ํ์ธ
@SpringBootTest
class DayonetestApplicationTests extends IntegrationTest {
@Autowired
private StudentScoreRepository studentScoreRepository;
@Autowired
private EntityManager entityManager;
@Test
void contextLoads() {
StudentScore studentScore = StudentScoreTestDataBuilder.passed()
.studentName("pcy") // ์ค๋ฒ๋ผ์ด๋ฉ ์ฌ์ฉ ๊ฐ๋ฅ
.build();
StudentScore savedStudentScore = studentScoreRepository.save(studentScore);
entityManager.flush();
entityManager.clear();
StudentScore queryStudentScore = studentScoreRepository.findById(savedStudentScore.getId()).orElseThrow();
Assertions.assertEquals(savedStudentScore, savedStudentScore);
}
}
• @Autowired: Spring์ ์์กด์ฑ ์ฃผ์ ์ ์ฌ์ฉํ์ฌ StudentScoreRepository์ EntityManager ์ธ์คํด์ค๋ฅผ ์ฃผ์ ํฉ๋๋ค.
• studentScoreRepository: StudentScore ์ํฐํฐ์ ๋ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ์ ์ํํ๋ ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋๋ค.
• entityManager: JPA์ ์ํฐํฐ ๋งค๋์ ๋ก, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ์ฐ์ ์ง์ ์ํํ ์ ์์ต๋๋ค.
๋ฉ์๋ ๋ด๋ถ ์ค๋ช
1. ํ ์คํธ ๋ฐ์ดํฐ ์์ฑ
• StudentScoreTestDataBuilder.passed(): StudentScore ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํ ๋น๋ ํจํด์ ์ฌ์ฉํฉ๋๋ค. ์ด๊ธฐ ์ํ๋ก passed() ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๊ธฐ๋ณธ ์ค์ ์ ์ ์ฉํฉ๋๋ค.
• studentName("pcy"): ํ์ ์ด๋ฆ์ “pcy”๋ก ์๋กญ๊ฒ ์ค์ ํฉ๋๋ค.
• build(): ์ค์ ๋ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก StudentScore ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.
2. ๋ฐ์ดํฐ ์ ์ฅ
3. ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ์
• entityManager.flush(): ํ์ฌ ์์์ฑ ์ปจํ ์คํธ์ ์๋ ๋ณ๊ฒฝ ์ฌํญ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ํฉ๋๋ค.
• entityManager.clear(): ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ด๊ธฐํํ์ฌ ์บ์๋ฅผ ๋น์๋๋ค.
4. ๋ฐ์ดํฐ ์กฐํ ๋ฐ ๊ฒ์ฆ
• studentScoreRepository.findById(savedStudentScore.getId()): ์ ์ฅ๋ StudentScore ์ํฐํฐ๋ฅผ ID๋ฅผ ๊ธฐ์ค์ผ๋ก ์กฐํํฉ๋๋ค.
• orElseThrow(): ํด๋น ID๋ก ์ํฐํฐ๋ฅผ ์ฐพ์ง ๋ชปํ๋ฉด ์์ธ๋ฅผ ๋ฐ์์ํต๋๋ค.
• Assertions.assertEquals(savedStudentScore, savedStudentScore): ์ ์ฅ๋ ์ํฐํฐ์ ์กฐํ๋ ์ํฐํฐ๊ฐ ๋์ผํ์ง ๊ฒ์ฆํฉ๋๋ค.
2. Github์ผ๋ก ํ์ธ
์ ์ฒด ํ์ผ ํ์ธ(ํ์ฌ ๋ณ๊ฒฝ๋ด์ญ)
[Github]
์ฐธ์กฐ : ์ธํ๋ฐ ๊ฐ์ [์ฅฌ์ฅฌ์ ํจ๊ป ํ๋ฃจ๋ง์ ๋๋ด๋ ์คํ๋ง ํ ์คํธ]
'๐ป FrameWork(ํ๋ ์์ํฌ) > SpringTEST(์คํ๋งํ ์คํธ)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
4-2 [ํตํฉTEST] ์ค์ DATA๋ก ์งํํ๋ TEST (0) | 2024.06.16 |
---|---|
3-6 [Mocktio] ๋ฆฌํฉํ ๋ง ์์ (0) | 2024.06.07 |
3-5 [Mocktio] ArugmentCaptor (๋ฐ์ดํฐ ์ ๋ ฅ ์ธ์ ํ์ธ) (0) | 2024.06.06 |
3-4 [Mocktio] Stubbing ํ ์คํธ(๊ฐ์ง ๋ฐ์ดํฐ ์์ฑ) (0) | 2024.06.06 |
3-3 [Mocktio] ํ์ ๊ฒ์ฆ TEST(๋ฉ์๋ ํธ์ถ ์ฌ๋ถ ํ์ธ) (0) | 2024.06.06 |