Testing HTTPS Requests with Wiremock and Robolectric

Prerequisites: OkHttp 3.x/4.x, Wiremock 2.x, Robolectric 4.9.x

// build.gradle.kts
dependencies {
  testImplementation("org.robolectric:robolectric:4.9.2"
  testImplementation("com.github.tomakehurst:wiremock-jre8-standalone:2.35.0")
  testImplementation("com.squareup.okhttp3:okhttp-tls:4.10.0")
}

Now for the actual test it’s important that your System Under Test (SUT) is able to configure it’s OkHttpClient instance:

class SomeHttpClient @VisibleForTesting internal constructor(client: OkHttpClient) {

  constructor() : this(OkHttpClient())

  fun execute(request: Request): Response =
    client.newCall(request).execute()
}

…so that in your test you can configure the needed SSL building blocks:

...
val handshakeCertificates = HandshakeCertificates.Builder()
  .addInsecureHost("localhost")
  .build()
val hostnameVerifier = HostnameVerifier { hostname, _ -> hostname == "localhost" }

val sut = SomeHttpClient(
  OkHttpClient.Builder()
    .sslSocketFactory(handshakeCertificates.sslSocketFactory(), handshakeCertificates.trustManager)
    .hostnameVerifier(hostnameVerifier)
    .build()
)
...

For the Robolectric setup, you need to ensure that you disable Robolectric’s conscrypt implementation, because this does not work well with Wiremock:

@RunWith(RobolectricTestRunner::class)
@ConscryptMode(ConscryptMode.Mode.OFF)
class SomeHttpClientTest {
  @get:Rule
  val wiremockRule = WireMockRule(wireMockConfig().dynamicHttpsPort())

  // val sut = ... (see above)

  @Test
  fun shouldRequestSomething() {
    stubFor(
      get(urlPathMatching("/echo$"))
        .willReturn(aResponse().withBody("Hello World!"))
      )
    )
    
    val response = sut.execute(
      Request.Builder()
        .url("${wiremockRule.baseUrl()}/echo")
        .get()
        .build()
      )

    // verify response
  }
}