Android Testing with Mocking

Abha Gupta
Walmart Global Tech Blog
5 min readJun 30, 2016

Android test automation in device farms is becoming a norm these days. Massive scaling of tests on different devices with optimal speed and utmost reliability has driven the testing community to come up with different mocking solutions. A large number of these solutions provide a hosted version of a mock server which runs as an instance and can be accessed to stub service endpoints using a mock server.

Often, automated web tests are executed in a continuous integration system like Jenkins, in which a master job creates multiple child jobs which each:

  • open a connection to a cloud service provider
  • build the code
  • start the mock server and app
  • interact with mocked endpoints to drive the tests

However, with android native apps, the challenge is little harder because there is no application server or a mock server to start. The APK file is what you have which is uploaded to the device in cloud to run the test. This creates a need of running mock server as a part of the APK itself.

There are number of mock servers which can do the job. We investigated a few of them and finally selected wiremock for reasons mentioned below.

Wiremock on Android

Wiremock runs over a Jetty container which makes it perfect candidate to run in Android application. The wiremock jar can be imported as a jar in an android project and the mock server can be started as following :

WireMockServer wireMockServer = new WireMockServer(8080);

Sam Edward’s blog gives us a good starting point to run wiremock on Android.

HTTPS endpoints

Wiremock supports HTTPS using Java Key Store (JKS) as default keystore type. However, android JVM does not support JKS and the default keystore type in Android is Bouncy Castle Keystore (BKS). Hence there are couple of steps that needs to be taken to get HTTPS working on Android with wiremock.

Here are the steps you can follow to get things going :

Note that these instructions are provided to generate a self-signed certificate which is not applicable for production environments.

Adding the service provider in your JVM

  • Open the file as root <JavaHome>/jre/lib/security/java.security
  • Add line security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider at the the end of all service.providers.
  • Save and exit the file.
  • Sometimes the JVM settings need a system restart to take effect.

Create a BKS Keystore

  • Download the Keystore Explorer or Portecle open source tools. Both the softwares work almost same.
  • From the menu, click “Create a New Keystore”
  • Select BKS-V1 as Keystore
  • You will see a new empty keystore created without a key pair.
  • Click Save.
  • You will be prompted for a password. Type ‘password’ for simplicity sake, because this is what Wiremock chooses by default. Type the same password for Confirm password field.
  • Save the Keystore with a filename. The file name can be any file name but sake of clarity, you can give a extension, e.g keystore_bks.
  • Generate a key part and self-signed certificate by following these steps:
  • Click on “Generate Key Pair” button on top menu
Generate Key Pair
  • A dialog box will appear. Select OK without changing anything.
  • A new dialog will appear
  • Click on the Book Icon as shown below:
  • Enter the information for certificate request after prompted :
  • Click OK after entering the complete information.
  • Click OK again after verification
  • Enter an Alias when prompted. You can enter ‘android_keystore’.
  • Enter the password here. This should match the password you entered. Our password is ‘password’
  • Click OK
  • At this time your BKS keystore should be ready to use.

Wiremock updates:

The changes done to wiremock to support BKS Keystore type are :

  • In class wiremock/src/main/java/com/github/tomakehurst/wiremock/jetty9/JettyHttpServer.java, ‘createHttpsConnector’ sets the keystore type by system’s keystore type.
private ServerConnector createHttpsConnector(
HttpsSettings httpsSettings,
JettySettings jettySettings) {
//Added to support Android https communication.
CustomizedSslContextFactory sslContextFactory = new CustomizedSslContextFactory();
sslContextFactory.setKeyStorePath(httpsSettings.keyStorePath());

sslContextFactory.setKeyManagerPassword(httpsSettings.keyStorePassword());
sslContextFactory.setKeyStoreType(httpsSettings.keyStoreType());
if (httpsSettings.hasTrustStore()) {
sslContextFactory.setTrustStorePath(httpsSettings.trustStorePath());
sslContextFactory.setTrustStorePassword(httpsSettings.trustStorePassword());
sslContextFactory.setTrustStoreType(httpsSettings.trustStoreType());
}
sslContextFactory.setNeedClientAuth(httpsSettings.needClientAuth());

HttpConfiguration httpConfig = createHttpConfig(jettySettings);
httpConfig.addCustomizer(new SecureRequestCustomizer());

final int port = httpsSettings.port();

return createServerConnector(
jettySettings,
port,
new SslConnectionFactory(
sslContextFactory,
"http/1.1"
),
new HttpConnectionFactory(httpConfig)
);
}

Note that code will receive the system’s keystore type automatically.

  • A new class CustomizedSslContextFactory.java is created which overrides ‘customize’ method since Android does not have the method ‘setEndpointIdentificationAlgorithm in SSLParams.java
public class CustomizedSslContextFactory extends org.eclipse.jetty.util.ssl.SslContextFactory  {

public void customize(SSLEngine sslEngine)
{
SSLParameters sslParams = sslEngine.getSSLParameters();
// sslParams.setEndpointIdentificationAlgorithm(_endpointIdentificationAlgorithm);
sslEngine.setSSLParameters(sslParams);

if (super.getWantClientAuth())
sslEngine.setWantClientAuth(super.getWantClientAuth());
if (super.getNeedClientAuth())
sslEngine.setNeedClientAuth(super.getNeedClientAuth());

sslEngine.setEnabledCipherSuites(super.selectCipherSuites(
sslEngine.getEnabledCipherSuites(),
sslEngine.getSupportedCipherSuites()));

sslEngine.setEnabledProtocols(super.selectProtocols(sslEngine.getEnabledProtocols(),sslEngine.getSupportedProtocols()));
}



}

Android App Updates

  • If you are building wiremock locally, wiremock jar is generated by running ./gradlew assemble . This gets saved in ‘builds/libs’.
  • Copy the wiremock jar in the Android projects under <root>/builds/libs directory.
  • In Android project’s build.gradle file, following dependencies needs to be added:
def versions = [
jackson: '2.6.1',
jetty : '9.2.13.v20150730'
]
dependencies {
compile "com.google.guava:guava:18.0"
compile "org.eclipse.jetty:jetty-server:$versions.jetty"
compile "org.eclipse.jetty:jetty-servlet:$versions.jetty"
compile "org.eclipse.jetty:jetty-servlets:$versions.jetty"

compile "com.fasterxml.jackson.core:jackson-core:$versions.jackson",
"com.fasterxml.jackson.core:jackson-annotations:$versions.jackson",
"com.fasterxml.jackson.core:jackson-databind:$versions.jackson"
compile "org.apache.httpcomponents:httpclient:4.5"
compile "org.skyscreamer:jsonassert:1.2.3"
compile "xmlunit:xmlunit:1.6"
compile "com.jayway.jsonpath:json-path:2.0.0"
compile "org.slf4j:slf4j-api:1.7.12"
compile "net.sf.jopt-simple:jopt-simple:4.9"

compile fileTree(dir: 'libs', include: 'wiremock-2.0.8-beta.jar')
androidTestCompile 'org.apache.httpcomponents:httpclient-android:4.3.5.1'
}
configurations {
all*.exclude group: 'commons-logging', module: 'commons-logging'
all*.exclude group: 'com.android.support', module: 'support-annotations'
all*.exclude group: 'asm', module: 'asm'
}
  • If you are directly using the published wiremock library, your build.gradle entry will be as follows :
androidTestCompile("com.github.tomakehurst:wiremock:2.0.8-beta") 
  • Push the BKS keystore generated earlier in emulator’s /sdcard/ location. To do this, start your emulator and run ‘adb push <keystore file> /sdcard/.
  • In the test code, start the Wiremock server as follows :
WireMockServer wireMockServer  = new WireMockServer(wireMockConfig().port(BuildConfig.PORT).httpsPort(9943).keystoreType("BKS").keystorePath("/sdcard/keystore_bks"));
  • Stub responses :
String uri = "/api/840dbdf2737a7ff9/conditions/q/CA/atlanta.json";

String jsonBody = asset(getApplication(), "atlanta-conditions.json");
assertFalse(jsonBody.isEmpty());
wireMockServer.stubFor(get(urlMatching(uri))
.willReturn(aResponse()
.withStatus(200)
.withBody(jsonBody)));
  • Access the stubbed asset at https protocol
String serviceEndpoint = "https://127.0.0.1:" + 9943;
  • Call the url (we are using OkHttpClient)
OkHttpClient okHttpClient = HttpsSettings.allowAllCertificates();
Request request = new Request.Builder()
.url(serviceEndpoint + uri)
.build();

Response response = okHttpClient.newCall(request).execute();

Example: https://github.com/abhagupta/AndroidHttpMockingExamples/blob/master/app/src/androidTest/java/com/handstandsam/httpmocking/tests/wiremock/WireMockApplicationTestCase.java is the test that imports the keystore and runs the test.

Summary

Mocking on Android is very handy for creating reliable tests. Wiremock provides this feature of running the server inside the app itself make it an excellent candidate to run it for android application.With the latest updates in wiremock, you can create a massive parallel test suite for android apps.

--

--