DevLog ๐Ÿ˜ถ

[Spring] ์Šคํ”„๋ง ํ…Œ์ŠคํŠธ - ํ…Œ์ŠคํŠธ ์ปจํ…์ŠคํŠธ ์บ์‹ฑ, @SpringBootTest, @WebMvcTest ๋ณธ๋ฌธ

Back-end/Spring

[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๋ž‘ ๋ฌธ๋ฒ•์ด ๊ฑฐ์˜ ๋น„์Šทํ•œ ๊ฒƒ ๊ฐ™๋‹ค. 

์•Œ์•„๋ณด๊ณ  ์‹ถ๋‹ค๋ฉด ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

 


 

ํ…Œ์ŠคํŠธ ์ชฝ์€ ํŒŒ๊ณ  ํŒ”์ˆ˜๋ก ๋‚ด์šฉ์ด ๋„ˆ๋ฌด ๋งŽ์•„์„œ ์ž๋ฅด๊ธฐ๊ฐ€ ํž˜๋“ค๋‹ค ใ… _ใ… 

๊ทธ๋ž˜๋„ ๊ธฐ์ˆ  ๋ถ€์ฑ„ ์ค‘ ํ•˜๋‚˜์˜€๋Š”๋ฐ ๊ฐ€๋ณ๊ฒŒ ์ž˜ ๋ณด๊ณ  ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ ๊ฐ™๋‹ค!

Comments