Runtime agnostic and isolated fragments unit tests

Runtime agnostic and isolated fragments unit testsJesper ÅmanBlockedUnblockFollowFollowingJan 6After having heard of the write-once-run-everywhere ambitions from last year’s Google IO testing presentations I was very excited to try it out.

Although Project Nitrogen is not released yet, I really wanted to take Robolectric 4.

0 out for a spin.

Version 4.

0 brings with it support for the Espresso API:s, which supposedly means that you can now write tests for android components that run both on and off device with exact same style.

Yum!I recently started to work on a video player pet project where my ambition was to have non-trivial isolated fragment unit tests in a shared test folder that would run both instrumented and with the Robolectric simulator on the JVM.

Testing fragments in isolation on Android has historically been a fairly messy ordeal, a couple of reasons for which being the following:They need an activity to live in to be able launch and move between states.

To have properly isolated tests, the class under test needs to have it’s dependencies injected, but we don’t have full control over instantiation.

These issues could be addressed before, but it would be a whole lot messier; basically you’d need to create an empty test container activity yourself that would provide capabilities to move between states.

That plus creating another test Application class that would be injected with another version of your Dagger2 application component to provide mocked dependencies.

Yuck.

Thankfully life is made easier by the new fragment testing tools from Google.

Anyway, enough chit chat, let’s get cracking!Shared test setupFirst of all we need to enable ourselves to use the same source files in both on and off device tests — the way we achieve this is by creating a shared test folder that is included in our test as well as androidTest source sets respectively, build.

gradle looks as follows:android { sourceSets { androidTest { java.

srcDirs += "src/sharedTest/java" kotlin.

srcDirs += "src/sharedTest/java" } test { java.

srcDirs += "src/sharedTest/java" kotlin.

srcDirs += "src/sharedTest/java" } }}The package structure looks like thisConfigurationA configuration like the following is needed to make the tests run:android { defaultConfig { testInstrumentationRunner "androidx.

test.

runner.

AndroidJUnitRunner" } // Robolectric resource processing/loading testOptions{ includeAndroidResources = true }}dependencies { implementation("androidx.

test.

core:1.

1.

0") implementation("androidx.

fragment:fragment-testing:1.

1.

0-alpha03") testImplementation("org.

mockito:mockito-core:2.

23.

4") testImplementation(“androidx.

test:runner:1.

1.

1”) testImplementation(“androidx.

test.

ext:junit:1.

1.

0”) testImplementation(“androidx.

test.

espresso:espresso-core:3.

1.

1”) testImplementation(“androidx.

test.

ext:truth:1.

1.

0”) testImplementation(“org.

robolectric:robolectric:4.

1”) androidTestImplementation("org.

mockito:mockito-core:2.

23.

4") androidTestImplementation("org.

mockito:mockito-android:2.

23.

4") androidTestImplementation(“androidx.

test:runner:1.

1.

1”) androidTestImplementation(“androidx.

test.

ext:junit:1.

1.

0”) androidTestImplementation(“androidx.

test.

espresso:espresso-core:3.

1.

0”) androidTestImplementation(“androidx.

test.

ext:truth:1.

1.

0”)}Finally the following line needs to be added to gradle.

propertiesandroid.

enableUnitTestBinaryResources=trueActual testingNow we’re ready to write some actual tests; with Jetpack, Google is releasing a new testing API in the androidx.

fragment:fragment-testing package that is called FragmentScenario — it essentially lets you launch your fragment in an empty container activity as well as move around between different lifecycle states, pretty neat huh?.Note that this only works with androidx.

app.

Fragment though.

A simple example looks like this:FragmentScenario.

launchInContainer() launches the fragment in an empty container and brings it to the resumed state, after which we can start making view assertions.

While this is all fine and dandy, the example is a bit trivial; what if we want to verify an interaction on some mocked dependency upon click?.We could do something like this:There is a problem with the injection of the viewModel property here; the onFragment callback is invoked after the fragment has been moved to the resumed state, this means that if the viewModel is used in, or anytime before the fragment’s onResume method, the test will fail.

Luckily the library provides a way for us to take control over fragment instantiation ourselves by doing the following:Now we have control over instantiation and we can inject our dependencies right after the instance is created, jolly good.

This is quite verbose though, we can make it more concise by using a convenience method that the library provides called launchFragmentInContainer — it looks like this:That looks better, now we have a nice and clean test.

ResultsAnd now, the question we’ve all been asking ourselves — does this actually run both on and off device?.I’m happy to announce that indeed it does!Instrumented:.

/gradlew -Pandroid.

testInstrumentationRunnerArguments.

class=com.

jeppeman.

jetpackplayground.

ui.

videodetail.

VideoDetailFragmentTest connectedCheckoutput:12:36:06 V/InstrumentationResultParser: Time: 5.

30212:36:06 V/InstrumentationResultParser: 12:36:06 V/InstrumentationResultParser: OK (1 test)BUILD SUCCESSFUL in 28sRobolectric:.

/gradlew test –tests "com.

jeppeman.

jetpackplayground.

ui.

videodetail.

VideoDetailFragmentTest"output:BUILD SUCCESSFUL in 11sBut don’t take my word for it, have a crack at it yourselves.

And that’s about it!.For more advanced use cases, head over to the project on github; there I’m playing with things such testing in landscape, MotionLayout and more.

There are also a couple of tests where FragmentScenario#moveToState(LifeCycle.

State) is being used if anyone is curious about that.

Happy testing folks!jeppeman/android-jetpack-playgroundPet project for cutting edge Android development with Jetpack – jeppeman/android-jetpack-playgroundgithub.

com.. More details

Leave a Reply