I'm using Robolectric 4.3.1 (testImplementation "org.robolectric:robolectric:4.3.1") to create an Android sqlite environment for my integration tests. My system uses OkHttp (implementation 'com.squareup.okhttp3:okhttp:3.14.7') for real HTTP requests. I'm not using MockWebServer.
After I upgraded to the Android 10 SDK, I had to update my unit test JVM to JDK 9, per the Robolectric instructions:
Running tests on Android API 29 now strictly requires a Java9 runtime or newer. If you are seeing errors about unsupported Java version when running tests on API 29 via Android Studio; you can use the 'JRE' field in the Run Configuration dialog to configure a newer Java runtime. See https://developer.android.com/studio/run/rundebugconfig for more background.
However, now my integration tests fail with:
java.lang.NullPointerException: No password supplied for PKCS#12 KeyStore.
at org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi.engineLoad(Unknown Source)
at java.base/java.security.KeyStore.load(KeyStore.java:1479)
at java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.loadKeyStore(TrustStoreManager.java:367)
at java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.getTrustedCerts(TrustStoreManager.java:315)
at java.base/sun.security.ssl.TrustStoreManager.getTrustedCerts(TrustStoreManager.java:59)
at java.base/sun.security.ssl.TrustManagerFactoryImpl.engineInit(TrustManagerFactoryImpl.java:51)
at java.base/javax.net.ssl.TrustManagerFactory.init(TrustManagerFactory.java:278)
at okhttp3.internal.Util.platformTrustManager(Util.java:640)
at okhttp3.OkHttpClient.<init>(OkHttpClient.java:228)
at okhttp3.OkHttpClient.<init>(OkHttpClient.java:202)
How can I make real HTTP calls again?
TLDR
to workaround this problem, set the
javax.net.ssl.trustStoreTypesystem property toJKS:Details
I found this workaround:
However, when I tried that workaround, all of my HTTP calls failed with the following
ConnectExceptioninstead:I suspect that
OkHttprightly denies all TLS connections because it doesn't trust them since the workaround suggests setting thejavax.net.ssl.trustStoretoNONE.I also tried specifying the default password for java truststores,
changeitvia this workaround:But then I got the following exception when trying to instantiate an
OkHttpClient:which was caused by:
This was a great clue. I started debugging the JDK 9 source with Android Studio, and I noticed that when I ran with
org.junit.runners.BlockJUnit4ClassRunner, myKeyStore.keystoreSpiwas asun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12, but when I ran withorg.robolectric.RobolectricTestRunnermyKeyStore.keystoreSpiwas aorg.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi$BCPKCS12KeyStore.According to
JEP-229:Thus, the classic
$JAVA_HOME/lib/security/cacertsis still a Java Key Store, which I could verify:However, since the JDK wants to default to
PKCS12keystores, the JDK'sDualFormatPKCS12will fall back to aJKSif reading a file asPKCS12fails. Bouncycastle assumes that when thejavax.net.ssl.trustStoreTypeispkcs12that is really what we mean.Thus, to workaround this problem, set the
javax.net.ssl.trustStoreTypesystem property toJKS:I filed issues with bouncycastle and robolectric about this.