pact-jvm icon indicating copy to clipboard operation
pact-jvm copied to clipboard

BaseMockServer crash with Robolectric

Open doancea opened this issue 4 years ago • 8 comments

When executing tests in my Android test target, using Robolectric I get the following error:

`java.lang.NoSuchMethodError: org.apache.http.conn.ssl.SSLSocketFactory.(Lorg/apache/http/conn/ssl/TrustStrategy;)V

at au.com.dius.pact.consumer.BaseMockServer.waitForServer(MockHttpServer.kt:88)
at au.com.dius.pact.consumer.BaseMockServer.runAndWritePact(MockHttpServer.kt:105)
at au.com.dius.pact.consumer.ConsumerPactRunnerKt.runConsumerTest(ConsumerPactRunner.kt:13)
at au.com.dius.pact.consumer.junit.BaseProviderRule.runPactTest(BaseProviderRule.java:163)
at au.com.dius.pact.consumer.junit.BaseProviderRule.access$100(BaseProviderRule.java:27)
at au.com.dius.pact.consumer.junit.BaseProviderRule$1.evaluate(BaseProviderRule.java:83)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:546)
at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:252)
at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)`

Robolectric may be a red herring here, as I get a "method not mocked" exception when running the same tests without the robolectric test runner. Attempting to test a configured retrofit 2 client. I'm trying to add Pact testing to my existing Android project, so I don't have any issues with the unit under test.

I've checked for dependency conflicts with httpclient in the project and found none. It simply looks like the org.apache.http.conn.ssl.SSLSocketFactory has no constructor taking TrustStrategy

doancea avatar Jun 18 '20 16:06 doancea

Damn Android, they have different versions of the Apache HTTP libraries.

uglyog avatar Jun 19 '20 00:06 uglyog

Anything we can do about it, or any workaround? I'm using Junit5 but for Robolectric to work I have to use the Junit4 Runner, which is where I run into this issue. It's looking more and more like I'll need to cut bait and find a different solution for contract testing my API clients :(

doancea avatar Jun 19 '20 15:06 doancea

Depends if you need HTTPS or not. If not, we can just use reflection to check the SSLSocketFactory method signature and not set up the factory if it is not supported.

uglyog avatar Jun 20 '20 03:06 uglyog

I had the same issue as well and here is a way to fix it:

  • first, verify that you are using the latest apache HTTP library by running the following ./gradlew dependencies
  • since apache http is deprecated you would need to add the following to manifest

<uses-library android:name="org.apache.http.legacy" android:required="false"/>

https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p

moetouban avatar Jan 25 '21 16:01 moetouban

I'm having the same issue.

The workaround provided by @moetouban didn't work for me

andreroggeri avatar Oct 14 '21 20:10 andreroggeri

I built a version locally without https support (Deleting the SSLSocketFactory and TrustStrategy lines) and now the MockServer starts.

andreroggeri avatar Oct 15 '21 13:10 andreroggeri

Ping @uglyog is there any workaround ?

I really don't want to maintain a local fork and probably this may be blocking other people :)

(If checking if the SSLSocketFactory is available before using it is an viable option I can provide a PR)

This is what I'm doing right now because I dont need http/SSL

diff --git a/consumer/src/main/kotlin/au/com/dius/pact/consumer/MockHttpServer.kt b/consumer/src/main/kotlin/au/com/dius/pact/consumer/MockHttpServer.kt
index 7c2b684a5..ce0a599ec 100755
--- a/consumer/src/main/kotlin/au/com/dius/pact/consumer/MockHttpServer.kt
+++ b/consumer/src/main/kotlin/au/com/dius/pact/consumer/MockHttpServer.kt
@@ -27,8 +27,6 @@ import org.apache.http.client.methods.HttpOptions
 import org.apache.http.config.RegistryBuilder
 import org.apache.http.conn.socket.ConnectionSocketFactory
 import org.apache.http.conn.socket.PlainConnectionSocketFactory
-import org.apache.http.conn.ssl.SSLSocketFactory
-import org.apache.http.conn.ssl.TrustSelfSignedStrategy
 import org.apache.http.entity.ContentType
 import org.apache.http.impl.client.HttpClientBuilder
 import org.apache.http.impl.conn.BasicHttpClientConnectionManager
@@ -99,14 +97,11 @@ abstract class BaseMockServer(val pact: RequestResponsePact, val config: MockPro
   private val requestMatcher = RequestMatching(pact.interactions)

   override fun waitForServer() {
-    val sf = SSLSocketFactory(TrustSelfSignedStrategy())
     val retryStrategy = CustomServiceUnavailableRetryStrategy(5, 500)
     val httpclient = HttpClientBuilder.create()
       .setConnectionManager(BasicHttpClientConnectionManager(RegistryBuilder.create<ConnectionSocketFactory>()
         .register("http", PlainConnectionSocketFactory.getSocketFactory())
-        .register("https", sf)
         .build()))
-      .setSSLSocketFactory(sf)
       .setServiceUnavailableRetryStrategy(retryStrategy)
       .build()

andreroggeri avatar Nov 24 '21 21:11 andreroggeri

@andreroggeri I don't use Roboelectric or Android, so I am unable to diagnose or test this.

A PR would be awesome.

uglyog avatar Nov 24 '21 22:11 uglyog