androidwiremockandroid-instrumentationmeizu

No interface method isAsyncStarted() error running wiremock android tests on a Meizu device


I have an Android app which performs a network request. I'm using wiremock for instrumentation tests and retrofit in the app.

The request is mocked like this:

        stubFor(
            get(urlMatching("/hello/world"))
                .willReturn(
                    aResponse()
                        .withStatus(HttpURLConnection.HTTP_OK)
                        .withHeader("Content-Type", "text/plain")
                        .withBody(expectedServerResponse)
                )
        )

The retrofit interface:

interface HelloWorldService {
    @GET("/hello/world")
    fun getHelloWorld(): Call<ResponseBody>
}

The application code which performs the request:

        val helloWorldService =
            Retrofit.Builder()
                .baseUrl(intent.getStringExtra(EXTRA_BASE_URL))
                .client((application as MyApplication).createRetrofitHttpClient())
                .build()
                .create(HelloWorldService::class.java)
        helloWorldService.getHelloWorld().enqueue(object : Callback<ResponseBody> {
            override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
                textView.text = if (response.isSuccessful) {
                    response.body()?.string()
                } else {
                    response.message()
                }
            }

            override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
                textView.text = t.message
            }
        })

When the mocked response's content length is large (over 256 characters), and the test is run on a Meizu Note 5, wiremock finds the response and looks like it's about to send the 200 response, as I see this in the logcat log:

Matched response definition:
{
  "status" : 200,
  "body" : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "headers" : {
    "Content-Type" : "text/plain"
  }
}

Response:
HTTP/1.1 200
Content-Type: [text/plain]
Matched-Stub-Id: [a45b21c9-95f6-45fa-a7c3-acf473485fb6]

However, the app actually receives an 500 error code, with this message in the error response:

No interface method isAsyncStarted()Z in class Ljavax/servlet/http/HttpServletRequest; or its super classes (declaration of 'javax.servlet.http.HttpServletRequest' appears in /system/framework/meizu2_jcifs.jar)

Solution

  • The problem is that wiremock and meizu both embed javax.servlet classes, but different (incompatible) versions. One workaround for this is to relocate (change the package names) of the javax.servlet classes embedded by wiremock.

    Instead of including wiremock like this:

    androidTestImplementation "com.github.tomakehurst:wiremock-standalone:2.23.2"
    

    Use the gradle shadow plugin and use the shadowed jar instead:

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
        }
    }
    apply plugin: com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
    import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
    android {
        dependencies {
            shadow "com.github.tomakehurst:wiremock-standalone:2.23.2"
            androidTestImplementation files("$buildDir/libs/shadow.jar")
        }
    }
    
     tasks.configureEach { theTask ->
        def taskName = theTask.name.toString()
        if (taskName =~ /generate.*Sources/) {
            theTask.dependsOn tasks.named("shadowJar")
        }
    }
    
     tasks.register("shadowJar", ShadowJar) {
        baseName = 'shadow'
        configurations = [project.configurations.shadow]
        relocate ('javax.servlet', 'com.example.wiremockexample.javax.servlet'){}
    }
    

    A full sample project explaining this problem in more detail is here: https://github.com/libon/WiremockMeizuError

    And a PR on the project implements this answer: https://github.com/libon/WiremockMeizuError/pull/1