๊ด€๋ฆฌ ๋ฉ”๋‰ด

๐‘†๐‘ข๐‘›๐‘ โ„Ž๐‘–๐‘›๐‘’ ๐‘Ž๐‘“๐‘ก๐‘’๐‘Ÿ ๐‘Ÿ๐‘Ž๐‘–๐‘›โœง

[Spring] MockWebServer ๋ณธ๋ฌธ

Mock์ด๋ž€?

  • ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๊ธฐ์— ์‹œ๊ฐ„, ๋น„์šฉ ๋“ฑ์˜ Cost๊ฐ€ ๋†’๊ฑฐ๋‚˜ ํ˜น์€ ๊ฐ์ฒด ์„œ๋กœ๊ฐ„์˜ ์˜์กด์„ฑ์ด ๊ฐ•ํ•ด ๊ตฌํ˜„ํ•˜๊ธฐ ํž˜๋“ค ๊ฒฝ์šฐ ๊ฐ€์งœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ํ•œ๋ฒˆ์— ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ๋‹ค๋ฅธ ๋„คํŠธ์›Œํฌ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“ฑ๋“ฑ ์ œ์–ดํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฒƒ๋“ค์— ์˜์กดํ• ๋•Œ ๊ฐ€์งœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ํ…Œ์ŠคํŠธํ•œ๋‹ค.

 


Mock ๊ฐ์ฒด๋Š” ์–ธ์ œ ํ•„์š”ํ•œ๊ฐ€?

  • ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์„ ์œ„ํ•œ ํ™˜๊ฒฝ ๊ตฌ์ถ•์ด ์–ด๋ ค์šด ๊ฒฝ์šฐ
  • ํ…Œ์ŠคํŠธ๊ฐ€ ํŠน์ • ๊ฒฝ์šฐ๋‚˜ ์ˆœ๊ฐ„์— ์˜์กด์ ์ธ ๊ฒฝ์šฐ
  • ํ…Œ์ŠคํŠธ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ
  • ๊ฐœ์ธ PC์˜ ์„ฑ๋Šฅ์ด๋‚˜ ์„œ๋ฒ„์˜ ์„ฑ๋Šฅ๋ฌธ์ œ๋กœ ์˜ค๋ž˜ ๊ฑธ๋ฆด์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

 

MockWebServer

  • MockWebServer๋Š” HTTTP Request๋ฅผ ๋ฐ›์•„์„œ Response๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ„๋‹จํ•˜๊ณ  ์ž‘์€ ๊ฐ€์งœ ์›น์„œ๋ฒ„์ด๋‹ค. 
  • ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ ์ค‘์— WebClient, RestTemplete ๋“ฑ๊ณผ ๊ฐ™์€ Client๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, Http๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ์˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ• ๋•Œ ์ด MockWebServer๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ํ•จ์œผ๋กœ์จ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. 
  • ์‹ค์ œ๋กœ Spring Team๋„ MockWebServer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธํ•˜๋ผ๊ณ  ๊ถŒ์žฅํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

 

 

์™ธ๋ถ€ API๋ฅผ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ์ด์œ 

  • ์™ธ๋ถ€ api๋Š” api ์„œ๋ฒ„์˜ ์ƒํƒœ์— ๋”ฐ๋ผ ์„ฑ๊ณตํ•˜๊ธฐ๋„ ํ•˜๊ณ  ์‹คํŒจํ•˜๊ธฐ๋„ ํ•œ๋‹ค. ๋˜ํ•œ ํ•ด๋‹น ์„œ๋ฒ„์˜ ๋ฐ˜ํ™˜ ๊ฐ’์˜ ๊ตฌ์กฐ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๊ธฐ์กด์— ์งœ๋†“์€ ๋กœ์ง๊ณผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ๋ฌด์šฉ์ง€๋ฌผ์ด๋‹ค.
  • ์™ธ๋ถ€ API๊ฐ€ ์ •์ƒ์ด๊ณ , ์™ธ๋ถ€ API๊ฐ€ ์ž˜ ์ž‘๋™ํ•œ๋‹ค๋Š” ๊ฐ€์ •ํ•˜์— ๋กœ์ง์„ ๋‹จ์œ„ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.

 

์„ค์ •

 

build.gradle ์„ค์ •

// mockWebServer
testImplementation('com.squareup.okhttp3:okhttp:4.10.0')
testImplementation('com.squareup.okhttp3:mockwebserver:4.10.0')

 

 

Mock Server ์ธ์Šคํ„ด์Šคํ™”

 

class KakaoAddressSearchServiceRetryTest extends AbstractIntegrationContainerBaseTest {

    @Autowired
    private KakaoAddressSearchService kakaoAddressSearchService

    @SpringBean 
    private KakaoUriBuilderService kakaoUriBuilderService = Mock()

    private MockWebServer mockWebServer

 

spock๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€๋‹ค.

Mock() ์‚ฌ์šฉํ•˜์—ฌ ์‹ค์ œ๋กœ ์นด์นด์˜ค api๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‘๋‹ต๊ฐ’์„ ๋ฐ›๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ๋กœ์ปฌํ˜ธ์ŠคํŠธ์— ๋– ์žˆ๋Š” ๋ชจํ‚น ์„œ๋ฒ„๋ฅผ ํ˜ธ์ถœํ•˜์š” ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.  ๋˜ํ•œ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋‚ด์— ์žˆ๋Š” ํ•ด๋‹น ์„œ๋น„์Šค๋ฅผ ๋ชจํ‚นํ•˜๊ธฐ์œ„ํ•ด์„œ @SpringBean ์„ ์‚ฌ์šฉํ•œ๋‹ค. spock์—์„œ๋Š” @springBean ์„ ์‚ฌ์šฉํ•œ๋‹ค.(mockbean์€ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋‚ด์˜ bean์„ ๋ชจํ‚นํ•œ๋‹ค.)

 

def setup() {
    mockWebServer = new MockWebServer()
    mockWebServer.start()
}

def cleanup() {
    mockWebServer.shutdown()
}

def "requestAddressSearch retry success"() {
    given:
    def metaDto = new MetaDto(1)
    def documentDto = DocumentDto.builder()
            .addressName(inputAddress)
            .build()
    def expectedResponse = new KakaoApiResponseDto(metaDto, Arrays.asList(documentDto))
    def uri = mockWebServer.url("/").uri() //mockwebserver์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” uri๋ฅผ ๋ฐ˜ํ™˜

    when:
    mockWebServer.enqueue(new MockResponse().setResponseCode(504))
    mockWebServer.enqueue(new MockResponse().setResponseCode(200)
            .addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .setBody(mapper.writeValueAsString(expectedResponse)))

    def kakaoApiResult = kakaoAddressSearchService.requestAddressSearch(inputAddress)
    def takeRequest = mockWebServer.takeRequest()

    then:
    2 * kakaoUriBuilderService.buildUriByAddressSearch(inputAddress) >> uri // mockwebserver uri๋ฐ˜ํ™˜
    // builduribyaddressSearch ๋ฉ”์„œ๋“œ๊ฐ€ ๋‘๋ฒˆ ํ˜ธ์ถœ๋œ๊ฒŒ ๋งž๋Š”์ง€ 2* ๋ฅผ ํ•ด์ค€๋‹ค
    takeRequest.getMethod() == "GET"
    kakaoApiResult.getDocumentList().size() == 1
    kakaoApiResult.getMetaDto().totalCount == 1
    kakaoApiResult.getDocumentList().get(0).getAddressName() == inputAddress

}

ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ ์‹คํ–‰์ „์— setup()๋ฉ”์„œ๋“œ ์‹คํ–‰๋˜๊ณ  mockwebserver()๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋™์ ์œผ๋กœ port๊ฐ€ ํ• ๋‹น์ด ๋˜๊ณ  url๋Š” localhost์—์„œ ์‹คํ–‰์ด ๋œ๋‹ค. cleanup()๋ฉ”์„œ๋“œ๋Š” ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๊ฐ€ ๋๋‚˜๊ณ  ๋‚˜๋ฉด ์‹คํ–‰ํ•˜๋ฉฐ mockserver๋ฅผ shutdown()ํ•œ๋‹ค. 

mockWebServer.enqueue()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด MockWebServer๋Š” ์—”ํ๋œ ์ˆœ์„œ๋Œ€๋กœ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค.

 

 

 

 

 

 

 

 

์ถœ์ฒ˜

https://www.devkuma.com/docs/mock-web-server/