【Kotlin】 Mock Web Server를 사용하여 API 단위 테스트를 작성하는 방법

Overview



검색 창에 검색 키워드를 입력하고 검색 버튼을 누르면 API를 통해 이미지를 표시하는 간단한 Android 애플리케이션을 만들었습니다.

디자인은 다음 그림과 같습니다.


  • View 레이어는 키워드를 받고 버튼 이벤트가 발화되면 Presenter에 알립니다.
  • Presenter 레이어는 View에서 전달된 키워드를 매개 변수로 Service 레이어의 API 메서드를 호출합니다.
  • 서비스 계층에서 외부 API (https://api.unsplash.com)에서 이미지 URI를 가져옵니다.
  • API 결과에 따라 Presenter가 가지고 있는 데이터 소스를 업데이트합니다.
  • Presenter는 데이터 소스 업데이트를 View에 알리고 View를 업데이트합니다.

  • 서비스 레이어의 단위 테스트에 okhttp3의 MockWebServer를 사용했습니다.

    그 때에 조금 막혔으므로, 사용법을 남깁니다.

    필요한 설정



    1. 라이브러리 도입



    이것들을 모듈의 build.gradle에 추가합니다.
    androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.0.0'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    

    2. 설정



    2.1 네트워크 권한 추가



    AndroidManifest.xml에 네트워크 권한을 추가합니다.
    <uses-permission android:name="android.permission.INTERNET" />
    

    2.2 localhost와의 통신 허용



    localhost와의 통신을 허용하려면 AndroidManifest.xml의 application에 다음 설정을 추가합니다.
    <application
      ...
      android:usesCleartextTraffic="true"
      ...>
    

    (어디까지나 개발 환경에서의 설정입니다.)

    (option) 2.3 testInstrumentRunner 설정



    모듈의 build.gradle 안에 testInstrumentationRunner"android.support.test.runner.AndroidJUnitRunner" 이면 "androidx.test.runner.AndroidJUnitRunner" 로 변경합니다.

    이는 지원하는 테스트 프레임워크에 따라 다릅니다. 자세한 내용은 여기 .

    지금까지 할 수 있으면 한 번 다시 빌드합니다.

    3. 테스트 파일 만들기



    테스트 대상이 되는 클래스(이번은 service 클래스)를 cmd+shift+t로 테스트 파일을 만듭니다.

    이번은 android 의 JUnitRUnner를 사용하므로 androidTest 이하에 추가합니다.

    4. 테스트 만들기



    mock web server를 사용하는 데는 크게 네 가지 단계가 있습니다.
  • mock 서버 세운다
  • 응답의 인스턴스 만들기
  • mock 서버에 인스턴스를 enqueue
  • 끝점을 두드리는

  • 순서대로 설명합니다.

    4.1 mock 서버의 인스턴스 생성



    테스트 클래스의 생성자 내에서 모의 ​​서버 인스턴스를 생성합니다.
    private val webServer = MockWebServer()
    

    @Before 등으로 모의 서버를 청취 상태로 만듭니다.
    webServer.start(8080)
    

    이제 localhost:8080에서 모의 ​​서버가 대기 중인 상태가 됩니다.

    4.2 응답 인스턴스를 만듭니다.


    MockResponse() 에서 응답 클래스를 생성합니다.
    private val successResponse = MockResponse().apply {
      setResponseCode(200)
      setHeader("Content-Type", "application/json")
      setHeader("Server", "Cowboy")
      setBody(sampleSuccessResponseData)
    }
    
    private val failResponse = MockResponse().apply {
      setResponseCode(401)
      setHeader("Content-Type", "application/json")
      setBody(sampleFailedResponseData)
    }
    
    private val sampleSuccessResponseData = """
    {
      "hoge": "fuga"
    }
    """.trimIndent()
    
    private val sampleFailedResponseData = """
    {
      "err": "unauthorized"
    }
    """.trimIndent()
    

    setResponseCode에서 응답 코드를 설정할 수 있습니다.

    setHeader에서 헤더를 설정할 수 있습니다.

    setBody에서 응답 본문을 설정할 수 있습니다.

    4.3 mock 서버에 응답을 enqueue


    webServer.enqueue(successResponse)
    webServer.enqueue(failResponse)
    

    액세스할 때마다 하나씩 응답이 dequeue됩니다.

    4.4 엔드포인트 치기



    Service 클래스에 엔드포인트를 설정합니다.

    제 경우에는 RetroFit2.0을 사용하고 있었으므로 다음과 같이했습니다.
    object UnsplashService {
    
        var END_POINT = "https://api.unsplash.com/"
    
        fun getImageURL(completion: <コールバック処理>, query: String) {
            Retrofit
                .Builder()
                .baseUrl(END_POINT)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(UnsplashAPI::class.java)
                .getImage(query)
                .enqueue(<コールバック処理>)
        }
    
        fun setEndPoint(url: String) {
            END_POINT = url
        }
    }
    
    setEndPoint 메소드를 살려두고, test할 때는,
    UnsplashService.setEndPoint("http://localhost:8080")
    

    로 모의 서버를 두드리도록 변경했습니다.

    또, 테스트 처리 후에는 서버를 떨어뜨리게 하는 것이, 행의가 좋은 것 같습니다.
    @After
    fun cleanUp() {
      webServer.shutdown()
    }
    

    보충



    모의 서버로 보내진 요청이 올바른지 테스트하고 싶다면 다음과 같이 요청을 검색할 수 있습니다.
    webServer.takeRequest().headers[<ヘッダー名>]
    

    좋은 웹페이지 즐겨찾기