Member preview

Instrumental Test: Record and replay your network payload

Ever wonder how nice if you could record your network fetch payload and replay it? This blog will talk about it, makes your Instrumental Test easier.

Background

In my previous blog, I mentioned about Instrumental Test, skipping the need of using Thread.sleep(..).

However, there’s still a limitation there, that is the network fetch from the server might change, and this will cause the result the test could fail from time to time, and need to be updated.

So it would be cool if we could record the payload fetch in the past, and replay it in the future in our test.

We could do this using a library by AirBnB, Okreplay.

How to set it up?

There’s a blog by Felipe Lima, which provide a great overview of it.

However, when trying to use it, and reading from the blog and the library documentation, I still got stuck in many areas. So writing this blog to clear out all the issues I faces, and also provide a working example how it is used.

1. Add the classpath

Add this into the main project build.gradle

classpath “com.airbnb.okreplay:gradle-plugin:$version”

2. Add the library

Add this into the main module build.gradle, e.g. app in this project

debugImplementation "com.airbnb.okreplay:okreplay:$version"
releaseImplementation "com.airbnb.okreplay:noop:$version"
androidTestImplementation "com.airbnb.okreplay:espresso:$version"
androidTestImplementation "com.android.support.test:rules:$androidTestLibraryVersion"
Note that: com.android.support.test:rules is very important, to enable the payload to be written.

Also remember to add the okreplay plugin to the build.gradle

apply plugin: 'okreplay'

3. Enable WRITE_EXTERNAL_STORAGE for the APP

OkReplay uses the SDCARD to record the payload. Because of this, we’ll need to provide the permission to do so.

a. Add to AndroidManifest.xml the needed permission.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

b. Add installOptions "-g" to the module build.gradle, android’s section

adbOptions {
installOptions "-g"
}

c. Add the GrantPermissionRule in your test class

companion object {
@ClassRule
@JvmField
val grantExternalStoragePermissionRule: GrantPermissionRule =
GrantPermissionRule.grant(
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
Note: this is not provided in the documentation directly, and it’s found in the issue of the github.

4. Inject the okReplayInterceptor to the OkHttp client

OkReplay uses OkHttp, leveraging it’s interceptor to help capture the payload. We’ll need to provide okRepalyInterceptor to the OkhttpClient

companion object {
val okReplayInterceptor = OkReplayInterceptor()

fun create(): WikiApiService {
val builder = OkHttpClient.Builder()
builder.addInterceptor(okReplayInterceptor)

val retrofit = Retrofit.Builder()
.addCallAdapterFactory(
RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://en.wikipedia.org/w/")
.client(builder.build())
.build()

return retrofit.create(WikiApiService::class.java)
}
}

5. Configure okReplay into the TestRule

For the test to run, we’ll need to configure OkReplay into the testRule

private val okReplayConfig = OkReplayConfig.Builder()
.tapeRoot(AndroidTapeRoot(
InstrumentationRegistry.getContext(), javaClass))
.defaultMode(TapeMode.READ_WRITE) // or TapeMode.READ_ONLY
.sslEnabled(true)
.interceptor(WikiApiService.okReplayInterceptor)
.build()

@get:Rule
val testRule = OkReplayRuleChain(okReplayConfig, activityRule).get()
Note: for the testRule, it has to be public. Setting it to private will result the payload not written out for some reason.

6. Annotate the test for Record and Replay

Add the @OkReplay to the test you want to Record and Replay.

@Test
@OkReplay(tape = "instrumental launch and search", mode = TapeMode.READ_WRITE)
fun launchAndSearch() {
Espresso.onView(ViewMatchers.withId(R.id.edit_search))
.perform(ViewActions.replaceText("Trump"))
Espresso.onView(ViewMatchers.withId(R.id.btn_search))
.perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.txt_search_result))
.check(ViewAssertions.matches(
ViewMatchers.withText("22908 result found")))
}
Note: The tape is for the saved payload file name, and it’s optional, as it will use the function name if tape is not given. The mode is optional too, and default (in the configuration) will be used if not given

Play and Record

Now you have set it up, all is good. All you need to do is to run the test.

If all is good, you should be able to have your payload saved in the SDCARD. In emulator, you’ll find the file created in below.

generic_x86_64:/sdcard/okreplay/tapes/<package>/<testfolder>/<payloadfilename>
Note: You could access using adb shell

Copy the recorded payload file out

In order to copy the recorded payload onto your project, you could use the okreplay provided gradle plugin, pullOkReplayTapes as shown below.

Once copied, the file will be copied to the <module>/assets/tapes/<TestFolder>/<PayloadFilename> in the project

It is in yaml format.

Restrict to replay only

Once you have the yaml file in your project, you could now change your test to replay only, so you could run your test OFFLINE, and predictably.

You just need to change the mode to mode = TapeMode.READ_ONLY.

With this, you’ll all done. Enjoy your recorded payload getting played over and over again in your test.


You could get the code from

In case you would like to know if it works fro Protocol Buffer… Yes it does! You could check out the project in the below blog, and checkout the feature/test branch of it.

I hope this post is helpful to you. You could check out my other interesting topics here.

Follow me on medium, Twitter or Facebook for little tips and learning on Android, Kotlin etc related topics. ~Elye~