DevLog ๐ถ
[Spring] ์คํ๋ง ํ ์คํธ - ํ ์คํธ ์ปจํ ์คํธ ์บ์ฑ, @SpringBootTest, @WebMvcTest ๋ณธ๋ฌธ
[Spring] ์คํ๋ง ํ ์คํธ - ํ ์คํธ ์ปจํ ์คํธ ์บ์ฑ, @SpringBootTest, @WebMvcTest
dolmeng2 2023. 5. 8. 00:50๐ฑ ๋ค์ด๊ฐ๊ธฐ ์
์ด์ ํฌ์คํ ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์คํฐ๋์์ ๋งก์ 'ํ ์คํธ' ํํธ์ ๋ํด์ ๋ธ๋ก๊ทธ์ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค ๐
ํ ์คํธ ์ชฝ์ ๊ณต์ ๋ฌธ์๋ ์๊ฐ๋ณด๋ค ๊ฐ๋ ์ฑ์ด ๋๋ฌด ์ ์ข์์ ์ต๋ํ ๊ฐ๋ตํ๊ฒ๋ง ํ์ด๋ณด์๋ค.
์์ง ๋ชจ๋ฅด๋ ๊ฒ ์ ๋ง ๋ง์ ๊ฒ ๊ฐ๋ค ใ ใ
๐ฑ ํตํฉ ํ ์คํธ vs ์ธ์ ํ ์คํธ
ํตํฉ ํ ์คํธ๋ ํน์ ์์ ์ ์ํํ๊ธฐ ์ํด, ์ธ๋ถ ์์ ๋ค๊ณผ ์ฐ๊ด๋์ด ์๋ค๋ฉด ํด๋น ์ธ๋ถ ์์ ๋ค์ ํฌํจํ์ฌ ๊ตฌ์ฑ ์์๊ฐ ์ ๋์๊ฐ๋์ง ํ ์คํธํ๋ ๋ฐฉ๋ฒ์ผ๋ก, @SpringBootTest ์ด๋ ธํ ์ด์ ์ ํ์ฉํ์ฌ ์งํํ๋ ๋ฐฉ๋ฒ์ด ๋ง๋ค. (๊ด๋ จ๋ ๋ชจ๋ ๋น์ ๊ฐ์ ธ์์ ํ ์คํธ ํ๋ ๋๋)
๋ฐ๋ฉด, ์ธ์ ํ ์คํธ์ ๊ฒฝ์ฐ ์ฌ์ฉ์์ ์๋๋ฆฌ์ค์ ๋ง์ถฐ ์ํํ๋ ํ ์คํธ์ด๋ค.
์ธ์ ํ ์คํธ์ ๋ฐฉ๋ฒ๋ก ์ผ๋ก E2E ํ ์คํธ๋ฅผ ํตํด ์ฃผ์ด์ง ์๋๋ฆฌ์ค์ ๋ฐ๋ผ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ชจ๋ ๊ณ์ธต์ด ์๋ก ์ ๋์ํ๋์ง ํ์ธํ ์ ์๋ค.
RestAssured๋ฅผ ํ์ฉํด์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐฉ์์ ๋ง์ด ๋ณด์๋ค.
์ฌ์ค ์ธ์ ํ ์คํธ์ E2E ํ ์คํธ๊ฐ ๋์ผํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ์๋๋ฐ, E2E ํ ์คํธ๊ฐ ์กฐ๊ธ ๋ ํฐ ๋ฒ์๋ผ๊ณ ์๊ฐํ ์ ์์ ๊ฒ ๊ฐ๋ค.
์ธ์ ํ ์คํธ๋ ์ฌ์ฉ์์ ์๋๋ฆฌ์ค์ ๋ง์ถ๊ธฐ ๋๋ฌธ์, ๊ทธ๊ฒ ๋จ์ ํ ์คํธ์ด๋ , ํตํฉ ํ ์คํธ์ด๋ ์ฌ์ฉ์์ ์๊ตฌ์ฌํญ์ ๋ง์ถ๋ค๋ฉด ๋ค ์ธ์ ํ ์คํธ๋ผ๊ณ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๐ฑ ํ ์คํธ ํ๋ ์์ํฌ
์คํ๋ง์ ํ
์คํธ์ ์ฌ์ฉ๋๋ ์ ํ๋ฆฌ์ผ์ด์
์ปจํ
์คํธ๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๊ณ , ํ
์คํธ์ ์ ์ฉํด์ฃผ๋ ๊ธฐ๋ฅ์ ๊ฐ์ง ‘ํ
์คํธ ํ๋ ์์ํฌ’๋ฅผ ์ ๊ณตํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ‘ํ
์คํธ ์ปจํ
์คํธ ํ๋ ์์ํฌ’๋ผ๊ณ ๋ถ๋ฅธ๋ค.
ํ
์คํธ ๋์ ๋ฉ์๋์ @Test๋ฅผ ๋ถ์ด๋ฉด ํด๋น ๋ฉ์๋๊ฐ ์ํ ํด๋์ค๋ ‘ํ
์คํธ ํด๋์ค’๋ก์ ๋์ํ๋ฉฐ, ๊ฐ ๋ฉ์๋๋ ๋
๋ฆฝ์ ์ธ ํ
์คํธ๊ฐ ๋๋ค.
ํ์ง๋ง, ํ
์คํธ๋ง๋ค ํ
์คํธ ์ปจํ
์คํธ๋ฅผ ๋งค๋ฒ ์๋ก ์์ฑํ๊ฒ ๋๋ค๋ฉด ์ค๋ฒํค๋๊ฐ ํฌ๊ธฐ ๋๋ฌธ์ ์ ์ฒด ํ
์คํธ์ ์คํ ์๋๊ฐ ๋๋ ค์ ธ์
๊ฐ๋ฐ์์ ์์ฐ์ฑ์ด ๋จ์ด์ง๋ค. ์ด๋๋ฌธ์ ํ
์คํธ ์ปจํ
์คํธ๋ ์์ ์ด ๋ด๋นํ๋ ํ
์คํธ ์ธ์คํด์ค์ ๋ํ ์ปจํ
์คํธ ๊ด๋ฆฌ ๋ฐ ์บ์ฑ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค!
๊ทธ๋์ ํ ์คํธ๋ฅผ ํ ๋ ํน์ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ๊ตฌ์ฑํ๊ณ ์ถ๋ค๋ฉด, ํด๋ ์ค ๋ ๋ฒจ์ @ContextConfiguration ์ด๋ ธํ ์ด์ ์ ์ ์ธํจ์ผ๋ก์ ๊ตฌ์ฑ ์ ๋ณด๋ฅผ ์ฃผ์ ํด์ค ์ ์๋ค.
๐ก ํ์ด๊ฐ @Import ์ด๋ ธํ ์ด์ ์ด๋ ๋ค๋ฅธ ๊ฑฐ๋๊ณ ๋ฌผ์ด๋ดค๋๋ฐ, ๋ด๋ถ์ ์ผ๋ก ์กฐ๊ธ ๊น๋ณด๋๊น ๋ฑ๋ก๋๋ ๋น์ด ์กฐ๊ธ ๋ค๋ฅด๋ค.
@Import์ ๊ฒฝ์ฐ importsCleanupPostProcessor๋ผ๋ ๋น์ด ์ถ๊ฐ๋ก ๋ฑ๋ก๋๋ ๊ฑฐ๋ฅผ ํ์ธํ๋ค.
๋ํ, @Import์ ๊ฒฝ์ฐ ๊ธฐ์กด ApplicationContext์ ํด๋น Component๋ฅผ ์ถ๊ฐํ๋ค๋ ๊ฒ์ด๊ณ , @ContextConfiguration์ ๊ฒฝ์ฐ๋ ์ด๋ค ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ์ฌ์ฉํ ์ง, ๊ทธ๋ฆฌ๊ณ XML ๊ฐ์ ๊ฑธ๋ก ์ค์ ๋ ํ ์ ์๋ค๋ ์ ์ด ์์ ๊ฒ ๊ฐ๋ค.
์ฌ์ค ๋ช ํํ๊ฒ ๋์ ์ฐจ์ด๋ฅผ ์ดํดํ๊ธฐ๋ ์ด๋ ต์ง๋ง... ์ ์ค์ผ์ด์ค๊ฐ ๋ค๋ฅธ ๊ฒ ๊ฐ๋ค.
๐ฌ ์ปจํ ์คํธ ์ค์ ์ ๋ณด ์ฃผ์ ํ๊ธฐ
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {AppConfig.class, TestConfig.class})
class MyTest {
}
๐ก @ExtendWith ์ด๋ ธํ ์ด์ ์ ๊ฒฝ์ฐ ์คํ๋ง ํ๋ ์์ํฌ์ Junit5๋ฅผ ์ฐ๋ํด์ฃผ๋ ์ญํ ์ผ๋ก์, @Test ๊ฐ์ ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋ถ์ฌ์ค์ผ ํ๋ค. (Junit4์์๋ @RunWith(SpringRunner.class)์ ์ฌ์ฉํ์์)
@ContextConfiguration ์ด๋ ธํ ์ด์ ์ ํ์ฉํ์ฌ ์ด๋ค ์ค์ ์ ๋ณด ํ์ผ์ ๊ตฌ์ฑํ ์ง ์ค์ ํ ์ ์๋ค.
์ฌ๊ธฐ์ classes ์ธ์๋ก ์ค AppConfig.class์ TestConfig.class์ ๋ฑ๋ก๋ ๋น ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ApplicationContext๊ฐ ๊ตฌ์ฑ๋๋ฉฐ, ์ด๋ ๊ฐ๊ฐ์ Configuration ํด๋์ค์ ๊ฒฝ์ฐ @Configuration ์ด๋ ธํ ์ด์ (์คํ๋ง ๋ถํธ๋ผ๋ฉด @TestConfiguration๋ ๊ฐ๋ฅ)์ด ๋ถ์ด ์๊ฑฐ๋, @Component, @Service, @Repository ๊ฐ์ ์คํ ๋ ์ค ํ์ ์ด๋ ธํ ์ด์ ์ด ๋ถ์ด์๋ ํด๋์ค๋ฉด ๊ฐ๋ฅํ๋ค.
@Configuration
public class Hello {
@Bean
public Nested nested() {
return new Nested();
}
static class Nested {
}
}
์์ ๊ฐ์ด static nested class๋ก ์ ์ธ๋์ด ์๋ ๋น์ ๋ฑ๋กํ๋ค๊ณ ์๊ฐํด๋ณด์.
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {Hello.class})
public class Sample {
@Autowired
private Nested nested;
@Test
void test() {
}
}
๊ทธ๋ผ ์์ ๊ฐ์ด Hello๋ผ๋ ์ค์ ์ ๋ณด ํด๋์ค๋ฅผ @ContextConfiguration์ผ๋ก ๋ถ๋ฌ์ค๊ฒ ๋๋ฉด, Hello๋ผ๋ Configuration ํด๋์ค์ ๋ํ ์ ๋ณด๋ฅผ ์ฝ์ด์ ๋ฑ๋ก๋์ด ์๋ Nested๋ผ๋ ๋น์ ๋ํด ์์กด๊ด๊ณ๋ฅผ ์ฃผ์ ๋ฐ์ ์ ์๊ฒ ๋๋ค.
๋ง์ฝ, @ContextConfiguration ์ด๋ ธํ ์ด์ ์ ์ ๊ฑฐํ๊ฒ ๋๋ฉด ์๋์ ๊ฐ์ด ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
๐ฑ ์ง๊ธ๊น์ง ๋๋ ์ปจํ ์คํธ ์ ๋ณด๋ฅผ ์ง์ ํ ์ ์ด ์๋๋ฐ...?
ํ์ง๋ง, ๋๋ ์ง๊ธ๊น์ง @ContextConfiguration์ด๋ผ๋ ์น๊ตฌ๋ฅผ ํ ๋ฒ๋ ํ ์คํธ ์ฝ๋์์ ์ฌ์ฉํ ์ ์ด ์์๋ค.
๊ทผ๋ฐ ์ด๋ป๊ฒ ํ ์คํธ๊ฐ ์ ์์ ์ผ๋ก ๋์์ ํ์๋ ๊ฒ์ผ๊น?
Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test
๊ธฐ๋ณธ์ ์ผ๋ก๋ @ContextConfiguration์ด๋ @SpringBootTest ์ด๋ ธํ ์ด์ ์ ์ง์ ํด๋ฌ๋ผ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
ํ์ง๋ง, ๋ง์ฝ โญ๏ธ ์คํ๋ง ๋ถํธ โญ๏ธ ๋ฅผ ์ฌ์ฉํ๊ณ , @SpringBootApplication ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ํด๋์ค๊ฐ ์กด์ฌํ๋ ํจํค์ง์ ํ์ ํจํค์ง์ ํ ์คํธ ํด๋์ค๋ฅผ ๋์๋ค๋ฉด, ์์์ ์ ์ ํ ์ ๋ณด๋ฅผ ์ฐพ๊ฒ ๋๋ค. (๋ณดํต ์ต์๋จ ํจํค์ง์ @SpringBootApplication์ ๋๋ ์ด์ ๊ฐ ์ฌ๊ธฐ์ ๋์จ๋ค)
Found @SpringBootConfiguration com.example.study.StudyApplication for test class com.example.study.hi.Sample
ํ ์คํธ ์ฝ๋๋ฅผ ๋๋ ค๋ณด๋ฉด ์์ ๊ฐ์ด @SpringBootConfiguration์ด๋ผ๋ ์ด๋ ธํ ์ด์ ์ ์ฐพ์๋ค๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ์ด๋ @SpringBootApplication ์ด๋ ธํ ์ด์ ์ด ์กด์ฌํ๋ ํด๋์ค๋ฅผ ์ฐพ์ผ๋ฉด์ ๋ด๋ถ์ ์ผ๋ก ์ค์ฒฉ๋์ด ์๋ ์ด๋ ธํ ์ด์ ์ธ @SpringBootConfiguration์ ์ฐพ์ ์ ์์๊ธฐ ๋๋ฌธ์ด๋ค.
์์ ๊ฐ์ด @SpringBootApplication ์ด๋ ธํ ์ด์ ์ ๊ฒฝ์ฐ ์ค์ฒฉ์ผ๋ก @SpringBootConfiguration์ด๋ผ๋ ์ด๋ ธํ ์ด์ ์ ๊ฐ์ง๊ณ ์๋ค. (@Configuration ์ด๋ ธํ ์ด์ ์ด๋ ๊ฑฐ์ ๋์ผํ๊ฒ ๋์ํ๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค.) ๋๋ถ์ ๋ณ๋์ ์ปจํ ์คํธ ์ ๋ณด๋ฅผ ์ง์ ํ์ง ์๋๋ผ๋ ์์์ ํด๋น ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ํด๋์ค๋ฅผ ์ค์ฌ์ผ๋ก ํ์ ํด๋์ค๋ค์ ์ค์ ์ ๋ณด๋ฅผ ์๋์ผ๋ก ์ฐพ์์ ๋ฑ๋กํ๊ฒ ๋๋ค.
๐ฑ Context Caching
ํ ์คํธ ์ปจํ ์คํธ ํ๋ ์์ํฌ๋ ํ ์คํธ์ ๋ํ ApplicationContext๋ฅผ ๋ก๋ํ๋ฉด, ํด๋น ์ปจํ ์คํธ๊ฐ ์บ์ฑ๋์ด ๋์ผํ ํ ์คํธ ์ปจํ ์คํธ ๊ตฌ์ฑ์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ํด๋น ์ปจํ ์คํธ๋ฅผ ์ฌ์ฌ์ฉํ๊ฒ ๋๋ค. ๋ณดํต ApplicationContext์ ๊ฒฝ์ฐ ์ด๋ฅผ ๋ก๋ํ๊ธฐ ์ํ configuration parameter์ ์กฐํฉ์ ํตํด ๊ณ ์ ํ๊ฒ ์๋ณ์ด ๊ฐ๋ฅํ๋ค.
configuration parameter๋ก๋ ๋ค์๊ณผ ๊ฐ์ ์ ๋ณด๋ค์ ์ฌ์ฉํ๋ค.
locations | @ContextConfiguration ์ด๋ ธํ ์ด์ ์ ์ธ์ |
classes | @ContextConfiguration ์ด๋ ธํ ์ด์ ์ ์ธ์ |
contextInitializerClasses | @ContextConfiguration ์ด๋ ธํ ์ด์ ์ ์ธ์ |
contextCustomizers | ContextCustomizerFactory๋ก๋ถํฐ ๋ก๋ |
contextLoader | @ContextConfiguration ์ด๋ ธํ ์ด์ ์ ์ธ์ |
parent | @ContextHierarchy |
activeProfiles | @ActiveProfile |
propertySourceLocations | @TestPropertySource |
propertySourceProperties | @TestPropertySource |
resourceBasePath | @WebAppConfiguration |
ํ ์คํธ ์ปจํ ์คํธ ํ๋ ์์ํฌ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ์ ์ ๋ณ์๋ก ์ ์ฅํ๋ฉฐ, ๋ง์ฝ ํ ์คํธ ์์ฒด๊ฐ ๋ณ๋์ ํ๋ก์ธ์ค์์ ์คํ๋๋ ๊ฒฝ์ฐ ๊ฐ ํ ์คํธ ์คํ ์ฌ์ด์ ์ ์ ์บ์๊ฐ ์ง์์ง๊ธฐ ๋๋ฌธ์ ์บ์ฑ ์์ ์ญ์ ํจ๊ป ๋นํ์ฑํ ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ์ปจํ ์คํธ ์บ์์ ํฌ๊ธฐ๋ 32๋ก ์ ํ๋์ด ์์ผ๋ฉฐ, ์ต๋ ํฌ๊ธฐ์ ๋๋ฌํ๊ฒ ๋๋ฉด LRU ์๊ณ ๋ฆฌ์ฆ์ ๋ฐ๋ผ ๊ฐ์ฅ ์ค๋๋ ์ปจํ ์คํธ๋ฅผ ์ ๊ฑฐํ๋ค.
์ค์ ๋ก ์ฝ๋๋ฅผ ํตํด ํ์ธํด๋ณด์. ์์์ ์ ์ธํ๋ Sample ํด๋์ค์ ๋์ผํ ์ค์ ์ ๋ณด๋ฅผ ๊ฐ์ง Sample2 ํด๋์ค๋ฅผ ์์ฑํด์ฃผ์๋ค.
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {Hello.class})
public class Sample {
@Autowired
private Nested nested;
@Autowired
ApplicationContext ac;
@Test
void test() {
System.out.println("test1 = " + ac);
}
}
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {Hello.class})
public class Sample2 {
@Autowired
private Nested nested;
@Autowired
ApplicationContext ac;
@Test
void test() {
System.out.println("test2 = " + ac);
}
}
๋ ๋ค GenericApplicationContext@75437611์ด๋ผ๋ ๋์ผํ applicationContext ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
@ContextConfiguration ์ด๋ ธํ ์ด์ ๋์ ์ @SpringBootTest๋ฅผ ์ฌ์ฉํ๋ ํ ์คํธ ํด๋์ค๋ฅผ ํ๋ ๋ ์ถ๊ฐํด๋ณด์.
@SpringBootTest
@ExtendWith(SpringExtension.class)
public class Sample3 {
@Autowired
private Nested nested;
@Autowired
ApplicationContext ac;
@Test
void test() {
System.out.println("test3 = " + ac);
}
}
ํ์ธํด๋ณด๋ฉด @SpringBootTest๋ฅผ ์ฌ์ฉํ test3๋ง ๋ค๋ฅธ ApplicationContext ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๐ก ๋ง์ฝ @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)์ ์ฌ์ฉํ๊ฒ ๋๋ค๋ฉด ์ค์ ์น ํ๊ฒฝ์ด ๊ตฌ์ฑ๋๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ์ปจํ ์คํธ ์ ๋ณด๋ฅผ ์ง์ ํ๊ฒ ๋๋ฉด ๋ค๋ฅธ ์น ์๋ฒ๊ฐ ๋์์ง๊ฒ ๋๋ค.
๐ฑ ์ปจํ ์คํธ ๊ฐ์ ์ด๊ธฐํํ๊ธฐ - @DirtiesContext
๋ง์ฝ ๊ฐ์ ๋ก ์ปจํ ์คํธ๋ฅผ ์ ๊ฑฐํ๊ณ ์ถ๋ค๋ฉด ํด๋์ค, ํน์ ๋ฉ์๋ ๋ ๋ฒจ์ @DirtiesContext ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํ๋ฉด ๋๋ค.
์ด๋ฌ๋ฉด ๋์ผํ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ์ฌ์ฉํ๋๋ผ๋ ํ ์คํธ ์คํ ์ ์ ๊ฐ์ ์ ์ผ๋ก ๊ธฐ์กด ์ปจํ ์คํธ๋ฅผ ์ ๊ฑฐํ๊ณ ์๋ก์ด ์ปจํ ์คํธ๋ฅผ ์คํํด์ค๋ค.
๐ฌ ์๋ก์ด ์คํ๋ง ์ปจํ ์ด๋์์ ํ ์คํธ๋ฅผ ์คํํ๊ณ ์ถ์ ๊ฒฝ์ฐ
// ํ์ฌ ํ
์คํธ ํด๋์ค ์ด์ ์ ์ปจํ
์คํธ๋ฅผ ๋ํฐ ์ฒ๋ฆฌ
@DirtiesContext(classMode = BEFORE_CLASS)
class FreshContextTests {
}
// ๊ฐ๊ฐ์ ํ
์คํธ ๋ฉ์๋ ์ ์ ์ปจํ
์คํธ๋ฅผ ๋ํฐ ์ฒ๋ฆฌ
@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)
class FreshContextTests {
}
// ํ์ฌ ํ
์คํธ ๋ฉ์๋ ์ด์ ์ ์ปจํ
์คํธ๋ฅผ ๋ํฐ ์ฒ๋ฆฌ
@DirtiesContext(methodMode = BEFORE_METHOD)
@Test
void testProcessWhichRequiresFreshAppCtx() {
}
๐ฌ ์คํ๋ง ์ปจํ ์คํธ๋ฅผ ๋๋ฝํ๋ ๋์์ ๋ด๋ถ์ ์ผ๋ก ์ํํ ๊ฒ ๊ฐ์ ๋
// ํ์ฌ ํด๋์ค ์ดํ์ ์ปจํ
์คํธ๋ฅผ ๋ํฐ ์ฒ๋ฆฌ
@DirtiesContext
class ContextDirtyingTests {
}
// ๊ฐ๊ฐ์ ํ
์คํธ ๋ฉ์๋ ํ์ ์ปจํ
์คํธ๋ฅผ ๋ํฐ ์ฒ๋ฆฌ
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
class ContextDirtyingTests {
}
// ํ์ฌ ํ
์คํธ ๋ฉ์๋ ์ดํ์ ์ปจํ
์คํธ๋ฅผ ๋ํฐ ์ฒ๋ฆฌ
@DirtiesContext
@Test
void testProcessWhichDirtiesAppCtx() {
}
๐ก ํ์ด๋ ์๊ธฐํ๋ฉด์ ์ธ์ @DirtiesContext๋ฅผ ์ฌ์ฉํ ์ ์์์ง ์ด์ผ๊ธฐ๋ฅผ ๋๋์ด๋ณด์๋ค.
๋ค๋ฅธ ๋ถ๋ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๊ธฐํ๋ฅผ ์ํด์ ์ฌ์ฉํ์ ๋ค๊ณ ๋ง์ํ์ จ๋๋ฐ, ์ฌ์ค @DirtiesContext์ ๊ฒฝ์ฐ ์๋ก์ด ์๋ฒ๋ฅผ ๋์ฐ๋ฉด์ (๊ธฐ์กด์ ์๋ฒ๋ฅผ ๋ซ๊ณ ) ์ธ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์๋กญ๊ฒ ๋์ฐ๊ฒ ๋๋ค ๋ณด๋๊น ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ด๊ธฐํ๋๋ค๊ณ ์๊ฐํ๋ค. ๊ทธ๋ ๋ค๋ฉด ๋ง์ฝ ์๋ฒ๋ฅผ ๋์๋ ์ด๊ธฐํ๋์ง ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋ค๋ฉด (ํ๋กํ์ผ๋ณ๋ก ๋ค๋ฅธ DB๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ...) @DirtiesContext์ ์ฌ์ฉํ๋๋ผ๋ ์ด๊ธฐํ๊ฐ ์ ๋ ๊ฒ ๊ฐ๋ค๊ณ ์๊ฐํ๋ค.
๋ฒจ๋ฉ ๊ฐ์ ๊ธ์ ๋ณด๋ฉด ์ฑ๊ธํค ๋น์ด ์ํ๋ฅผ ๊ฐ์ง๊ณ ์๋ ๊ฒฝ์ฐ ๋ด๋ถ์ ์ผ๋ก ํด๋น ๋น์ ์กฐ์ํ์ ๋ applicationContext๋ฅผ ๊ฐ์ ์ ์ผ๋ก ์ด๊ธฐํํ๋ ค๋ฉด ์ฌ์ฉํ๋ผ๋ ๋ง์ด ์์๋๋ฐ, ์ ์ด์ ์ฑ๊ธํค ๋น์ด ์ํ๋ฅผ ๊ฐ์ง๊ณ ์๋ ๊ฒ ์์ฒด๊ฐ ์๋ชป๋ ์ค๊ณ๋ผ๋ ์๊ฐ๋ ๋ค๊ณ ... ์ด๋ ต๋ค ๐ฅฒ ์์ผ๋ก ์ฌ์ฉํ ์ผ์ด ์์๊น?... ๋ชจ๋ฅด๊ฒ ๋ค.
๐ฑ ์คํ๋ง ๋ถํธ์ ํ ์คํธ
์ฌ์ค ์์์ ์ธ๊ธํ ๋ด์ฉ์ ๋จ์ํ '์คํ๋ง ํ๋ ์์ํฌ'๋ฅผ ์ฌ์ฉํ์ ๋์ ์ผ์ด๋ค.
ํ์ง๋ง, ์คํ๋ง ๋ถํธ์์๋ ์ ํ๋ฆฌ์ผ์ด์ ํ ์คํธ ์ ๋์์ด ๋๋ ๋ค์ํ ์ ํธ๋ฆฌํฐ์ ์ด๋ ธํ ์ด์ ์ ์ง์ํ๋ค.
ํนํ, spring-boot-starter-test dependency๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด Junit, AssertJ ๊ฐ์ ์ ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค๋ ํจ๊ป ์ฌ์ฉํ ์ ์๋ค!
๐ฌ @SpringBootTest
์์์ ์ธ๊ธํ๋ ์คํ๋ง ํ๋ ์์ํฌ ํ์ค ์ด๋
ธํ
์ด์
์ธ @ContextConfiguration ๋์ @SpringBootTest ์ด๋
ธํ
์ด์
์ ํ์ฉํ ์ ์๋ค. @SpringBootTest ์ ๋
ธํ
์ด์
์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก๋ ์๋ฒ๋ฅผ ์์ํ์ง ์์ง๋ง, webEnvironment ์ต์
์ ํตํด์ ์ด๋ค ์์ผ๋ก ํ
์คํธ ํ๊ฒฝ์ ์กฐ์ฑํ ์ง ์ค์ ํ ์ ์๋ค.
MOCK (๊ธฐ๋ณธ๊ฐ)
: ApplicationContext์ ๋ก๋๋ผ๊ณ ๊ฐ์ง ์น ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ค. ๋ด์ฅ ์๋ฒ๊ฐ ์์๋์ง ์์ผ๋ฉฐ, mock-based์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์คํธ๋ฅผ ํ๊ธฐ ์ํด์ @AutoConfigureMockMvc ๋๋ @AutoConfigureWebTestClient์ ํจ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
RANDOM_PORT
WebServerApplicationContext๋ฅผ ๋ก๋ํ๊ณ , ์ค์ ์น ํ๊ฒฝ์ ์ ๊ณตํ๋ค. ๋ด์ฅ ์๋ฒ๊ฐ ์คํ๋๊ณ , ์์์ ํฌํธ์์ ์คํ๋๋ค.
- ๋ง์ฝ, ์ฌ์ฉํ ํฌํธ ์ ๋ณด๊ฐ ๊ถ๊ธํ๋ค๋ฉด @LocalServerPort ์ด๋ ธํ ์ด์ ์ ํตํด์ ํด๋น ํฌํธ ์ ๋ณด๋ฅผ ์ป์ด์ฌ ์ ์๋ค. (@Value("${local.server.port}")์ ์์ ํ ๋์ผํ ์ญํ ์ ํ๋ค.)
DEFINED_PORT
WebServerApplicationContext๋ฅผ ๋ก๋ํ๊ณ ์ค์ ์น ํ๊ฒฝ์ ์ ๊ณตํ๋ฉฐ, ๋ด์ฅ ์๋ฒ๊ฐ ์คํ๋์ง๋ง application.yml์ ์ง์ ํ ํฌํธ๋ ๊ธฐ๋ณธ ํฌํธ์ธ 8080์ ์ฌ์ฉํ๋ค.
NONE
ApplicationContext๋ฅผ ๋ก๋ํ์ง๋ง ์น ํ๊ฒฝ์ ๊ตฌ์ถํ์ง ์๋๋ค.
โญ๏ธ ๋ง์ฝ, @Transactional์ ํ ์คํธ ๋ฉ์๋์ ์ ์ฉํ ๊ฒฝ์ฐ, ๊ฐ ํ ์คํธ ๋ฉ์๋๊ฐ ๋๋ ๋ ๋กค๋ฐฑ๋๋ค. ํ์ง๋ง RANDOM_PORT, DEFINED_PORT๋ฅผ ํตํด ์ค์ ์น ํ๊ฒฝ์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ํธ๋์ญ์ ์ด ์กํ ํ ์คํธ ๋ฉ์๋์ ์ค๋ ๋์ ์น ์๋ฒ์ ์ค๋ ๋๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ๋ณ๋์ ํธ๋์ญ์ ์ ์คํ๋์ด ํ ์คํธ ๋ฉ์๋์์ ์คํ๋ ๋ฉ์๋์ ๊ฒฝ์ฐ ํธ๋์ญ์ ๋กค๋ฐฑ์ด ๋์ง ์๋๋ค.
๐ฌ @WebMvcTest
Spring MVC ์ธํ๋ผ๋ฅผ ์๋์ ๊ตฌ์ฑํ๊ณ , ์น ๊ณ์ธต์์ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ๋ค์ ์ค์บํ๋ค.
์ด๋ ์ค์บ๋๋ ์ปดํฌ๋ํธ๋ @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer, WebMvcRegistrations ๋ฐ HandlerMethodArgumentResolver ๋ฑ์ด ์ฌ์ฉ๋๋ฉฐ, ์ฐ๋ฆฌ๊ฐ ํํ ์ฌ์ฉํ๋ @Service, @Component, @Repository๊ฐ ๋ถ์ ํด๋์ค๋ค์ ์ค์บ ๋์์ด ์๋๋ค.
์ด๊ฒ ๋ฌด์จ ์๋ฆฌ์ธ์ง ์ดํดํ๊ธฐ ์ด๋ ค์ธ ์ ์๋ค. ์ฝ๋๋ฅผ ํตํด ํ์ธํด๋ณด์.
@Controller
public class SampleController {
void test() {
}
}
ํ ์คํธ์ฉ ์ปจํธ๋กค๋ฌ์ด๋ค. ๊ทธ๋ฆฌ๊ณ , ํด๋น ์ปจํธ๋กค๋ฌ๋ฅผ ํ ์คํธํ๊ธฐ ์ํ ํ ์คํธ์ฉ ๋ฉ์๋๋ฅผ ์์ฑํด๋ณด์.
@WebMvcTest
class SampleControllerTest {
@Autowired
private SampleController sampleController;
@Test
void test() {
}
}
๋จ์ํ @WebMvcTest๋ง ์์ฑํ๊ณ , @Autowired๋ฅผ ํตํด ์ฃผ์ ๋ฐ์ผ๋ฉด ์๋ฌด ์ค๋ฅ ์์ด ์ ๋์ํ๋ค.
ํ์ง๋ง, SampleController ํด๋์ค์ ์ด๋ ธํ ์ด์ ์ @Service๋ก ๋ง๋ค์ด๋ณด์.
@Service
public class SampleController {
void test() {
}
}
Error creating bean with name 'com.example.study.SampleControllerTest': Unsatisfied dependency expressed through field 'sampleController': No qualifying bean of type 'com.example.study.SampleController' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
๊ทธ๋ฌ๋ฉด ์์ ๊ฐ์ด ์ ์ ํ ๋น์ด ์๋ค๋ ์ค๋ฅ ๋ฉ์์ง๊ฐ ๋์จ๋ค.
์ฆ, @Service์ ๊ฒฝ์ฐ ์ค์บ ๋์์ด ์๋๊ธฐ ๋๋ฌธ์ ๋น์ผ๋ก ์์ฑ๋์ง ์์ ์์กด ๊ด๊ณ๋ฅผ ์ฃผ์ ๋ฐ์ ์ ์๋ ๊ฒ์ด๋ค.
๊ทธ๋์ ๋๋ ์์ ๊ฐ์ ๊ฒฝ์ฐ์ ๋ณดํต 2๊ฐ์ง ์๋ฃจ์ ์ ์ฌ์ฉํ์๋ค.
1. ์ธ์๋ก ๋์ ํด๋์ค๋ฅผ ์ง์ ํด์ฃผ๊ธฐ
@WebMvcTest(SampleController.class)
class SampleControllerTest {
@Autowired
private SampleController sampleController;
@Test
void test() {
}
}
2. Mock ๊ฐ์ฒด๋ก ์ ์ธํด์ฃผ๊ธฐ
@ExtendWith(MockitoExtension.class)
class SampleControllerTest {
@InjectMocks
private SampleController sampleController;
@Test
void test() {
}
}
๊ฐ์ธ์ ์ผ๋ก ๋ฏธ์ ์ ์งํํ๋ฉด์ ํ์ด์๊ฒ @WebMvcTest์ ๊ฒฝ์ฐ ์ปจํธ๋กค๋ฌ ๊ณ์ธต์ ํ ์คํธํ ๋ ์ฌ์ฉํ๋ ๊ฒ ์ข์ ๊ฒ ๊ฐ๋ค๋ ์ด์ผ๊ธฐ๋ฅผ ๋ค์์ด์, ๋ ๋ฒ์งธ ๋ฐฉ๋ฒ์ผ๋ก ์๋น์ค ๋ ์ด์ด๋ฅผ ํ ์คํธํ๊ณค ํ๋ค.
๐ฌ @JdbcTest
@JdbcTest ์ด๋ ธํ ์ด์ ์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ด๋น ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ JdbcTemplate ๋น์ ๊ตฌ์ฑํ๋ค.
DB ๋ ์ด์ด๋ฅผ ํ ์คํธํ ๋ ๋ง์ด ์ฌ์ฉํ๋ฉฐ, ๋ด๋ถ์ ์ผ๋ก @Transactional ์ด๋ ธํ ์ด์ ์ด ์ ์ธ๋์ด ์๊ธฐ ๋๋ฌธ์ ํด๋์ค ๋ ๋ฒจ์ ๋ถ์ด๋ฉด ๊ฐ ํ ์คํธ ๋ฉ์๋๊ฐ ์ข ๋ฃ๋ ๋ ํจ๊ป ๋กค๋ฐฑ๋๋ค.
์ฌ๊ธฐ์ @AutoConfigureJdbc ์ด๋ ธํ ์ด์ ์ ํตํด์ Jdbc ์ ๊ด๋ จ๋ ๋น๋ค๋ง ์ปจํ ์คํธ๋ก ๋ฑ๋ก๋๋ค.
๋ํ, @AutoConfigureTestDatabase ์ด๋ ธํ ์ด์ ๋๋ถ์ ์ธ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค.
์ฐธ๊ณ ๋ก, ์ด ์ด๋ ธํ ์ด์ ์ ๊ฒฝ์ฐ replace ์ต์ ์ ๋ฐ๋ผ์ ์ธ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ ์ง ๋ง์ง ๊ฒฐ์ ํ ์ ์๋ค.
@JdbcTest
@AutoConfigureTestDatabase
class SampleControllerTest {
@Test
void test() {
}
}
๊ฐ๋จํ๊ฒ ์ดํด๋ณด์. ํด๋น ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ ๊ฒฐ๊ณผ์ด๋ค. (์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ์๋ @JdbcTest์ ์ํด์ ์ง์ ๋ ๊ฒ๊ณผ ๊ฐ์ ํจ๊ณผ)
- ์ฐธ๊ณ ๋ก, Replace ์ต์ ์ ๊ธฐ๋ณธ์ ์ผ๋ก Any์ด๋ค. Any์ ๊ฒฝ์ฐ ์๋์ผ๋ก ๊ตฌ์ฑ๋ ์ ๋ณด๋ฅผ ์ฌ์ฉํ๊ฑฐ๋, ํน์ ์๋์ผ๋ก ๊ตฌ์ฑ๋ ์ ๋ณด๋ฅผ ์ฌ์ฉํ๋ค.
ํ์ธํด๋ณด๋ฉด ๋ค๋ฅธ ์ธ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
@JdbcTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class SampleControllerTest {
@Test
void test() {
}
}
๋ฐ๋ฉด์, Replace๋ฅผ NONE์ผ๋ก ํด๋ณด์.
์ด๋๋ application.yml์ ์ ์ํ database๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ค.
๐ฌ @DataJdbcTest
@DataJdbcTest์ ๊ฒฝ์ฐ @JdbcTest์ ๊ฑฐ์ ๋น์ทํ๊ธด ํ๋ค.
ํ์ง๋ง ๋ฑ ํ ๊ฐ์ง ๋ค๋ฅธ ์ ์ด ์๋๋ฐ, @AutoConfigureDataJdbc๋ผ๋ ์ด๋ ธํ ์ด์ ์ด๋ค.
์ด๋ Spring Data JDBC repositories๋ฅผ ์ฌ์ฉํ๋ ํ ์คํธ์์ ์ฌ์ฉํ๋ค.
์ฌ์ฉํด๋ณธ ์ ์ด ์์ด์ ์ข ์ฐพ์๋ดค๋๋ฐ, JPA๋ ๋ฌธ๋ฒ์ด ๊ฑฐ์ ๋น์ทํ ๊ฒ ๊ฐ๋ค.
์์๋ณด๊ณ ์ถ๋ค๋ฉด ๊ณต์ ๋ฌธ์๋ฅผ ๋ณด๋ ๊ฒ๋ ์ข์ ๊ฒ ๊ฐ๋ค.
ํ ์คํธ ์ชฝ์ ํ๊ณ ํ์๋ก ๋ด์ฉ์ด ๋๋ฌด ๋ง์์ ์๋ฅด๊ธฐ๊ฐ ํ๋ค๋ค ใ _ใ
๊ทธ๋๋ ๊ธฐ์ ๋ถ์ฑ ์ค ํ๋์๋๋ฐ ๊ฐ๋ณ๊ฒ ์ ๋ณด๊ณ ๋์ด๊ฐ๋ ๊ฒ ๊ฐ๋ค!