Retrofit2 MockServer test case 작성

2023. 4. 27. 18:02android

이번 포스트에서는 Retrofit2를 가지고 RESTful Api 이용시 어떻게 test 하는지 알아보는 시간을 가져보겠습니다. 무조건 이 방법이 맞는건 절대! 아닙니다. 저도 검색하고 적용하는 시행착오를 통해 하나의 방법을 알게되어 소개해드립니다. Retrofit2를 이용하는 방식과 구조 과정은 이해하고 있다는 전제 하에 진행하겠습니다.

 

Dependency (retrofit2는 있다고 전제 하에 추가한 dependency입니다.)

//coroutine Testing
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'

//mock server
testImplementation 'com.squareup.okhttp3:mockwebserver:4.10.0'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.10.0'

//junit4 testing
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'

//core Testing
androidTestImplementation 'androidx.test:core:1.5.0'

전체 테스트 코드

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class Retrofit2Test {

    private lateinit var server: MockWebServer
    private lateinit var weatherApi: WeatherApi
    private lateinit var retrofit: Retrofit


    /*
    SetUP함수
    server는 mock 웹서버 인스턴스로 초기화합니다.
    server.start()
    start()함수는 주어진 port에 맞는 loopback interface 서버를 실행시킵니다.
    (loopback interface란 실제로는 존재하지 않는 인터페이스)
    retrofit을 만들어줍니다. url설정은 server의 url을 "/"로 간단하게 가상의 url을 설정합니다.

    api는 실제로 서비스 인터페이스로 만들어둔 api를 retrofit을 이용해 api의 엔드포인트의 구현체를 생성합니다.

     */
    @Before
    fun setUp() {
        server = MockWebServer()
        server.start()

        retrofit = Retrofit.Builder()
            .baseUrl(server.url("/"))
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        weatherApi = retrofit.create(WeatherApi::class.java)

    }

    @After
    fun tearDown(){
        server.shutdown()
    }

    /*
    test함수
    response는 테스트했을 때 받을 값입니다. json 형태로 들어옵니다.
     */
    @Test
    fun getCurrentWeatherTest() = runTest {
        val response = """
            {
              "response": {
                "header": {
                  "resultCode": 0,
                  "resultMsg": "성공"
                },
                "body": {
                  "dataType": "JSON",
                  "items": {
                    "item": [
                      {
                        "baseDate": "20230426",
                        "baseTime": "0500",
                        "category": "T1H",
                        "fcstDate": "20230426",
                        "fcstTime": "0600",
                        "fcstValue": "10",
                        "nx": 60,
                        "ny": 127
                      }
                    ]
                  }
                }
              }
            }
        """.trimIndent()

        server.enqueue(MockResponse().setBody(response))

        val weather = weatherApi.getCurrentWeather(
            baseDate = "20230426",
            baseTime = "0630",
            nx = 55,
            ny = 127
        )

        assertNotNull(weather)
        assertEquals(weather.response.header.resultCode, 0)
        assertEquals("성공", weather.response.header.resultMsg)
        assertEquals("JSON", weather.response.body.dataType)
        assertNotNull(weather.response.body.items.item)

    }


}

각 함수에 주석을 달아서 설명을 첨부하였습니다.

 

@Before

setUp 함수에서 테스트 전 준비를 위해 초기화를 진행합니다.

 

api 같은 경우는 실제 날씨( 구 동네예보) 공공데이터 api를 받아오기 때문에 그에 맞춰서 작성해줬습니다.

 

API

interface WeatherApi {
    @GET("getVilageFcst?serviceKey=${Constants.API_KEY}")
    suspend fun getTodayWeather(
        @Query("dataType") dataType : String = "json",
        @Query("numOfRows") numOfRows : Int = 60,
        @Query("pageNo") pageNo : Int = 1,
        @Query("base_date") baseDate : String,
        @Query("base_time") baseTime : String,
        @Query("nx") nx : Int,
        @Query("ny") ny : Int
    ) : Weather

    @GET("getUltraSrtFcst?serviceKey=${Constants.API_KEY}")
    suspend fun getCurrentWeather(
        @Query("dataType") dataType: String = "json",
        @Query("numOfRows") numOfRows: Int = 580,
        @Query("pageNo") pageNo: Int = 1,
        @Query("base_date") baseDate: String,
        @Query("base_time") baseTime: String,
        @Query("nx") nx: Int,
        @Query("ny") ny: Int
    ): Weather
}

 

위와 같이 작성하였구요.  그래서 @Test함수의 weather 변수에 api를 통해 데이터를 받아옵니다. 먼저 하나의 api 함수만 테스트 해보았습니다. Test 함수 안에 보시면 아래와 같이 적혀있습니다. weather는 api에 함수를 실행시킨것 입니다.

 

val weather = weatherApi.getCurrentWeather(
    baseDate = "20230426",
    baseTime = "0630",
    nx = 55,
    ny = 127
)

assertNotNull(weather)
assertEquals(weather.response.header.resultCode, 0)
assertEquals("성공", weather.response.header.resultMsg)
assertEquals("JSON", weather.response.body.dataType)
assertNotNull(weather.response.body.items.item)

 

assertNotNull 은 null인지 아닌지 테스트하고

제대로 나오는지 확인을 위한 테스트가 또 그 밑으로 여러개 있습니다. 

 

이런 식으로 테스트를 통해 Retrofit2의 api 가 제대로 작동하는지 확인해보았습니다.

여기에 있제 Repository 테스트를 하고 viewmodel test를 하면 됩니다. 

 

이상으로 retrofit2 test에 대한 글을 읽어주셔서 감사하고 틀린 부분은 지적해주시면 감사하겠습니다~