Isolate your Fragments…just for testing
Android testing has been traditionally hard. It is difficult and laborious to correctly architect your application such that your business logic and UI are properly de-coupled and can be tested, although the new Android architecture components will help a lot.
Most projects that I’ve seen, use Espresso to perform UI integration testing. But I have noticed that most projects, while testing, perform user flows. For instance, if your app has a login screen and, after login a list of items. Most projects, in order to test the list screen, would, first login and then test the list. This is what I call ‘user flow’.
This example is very simple, but you can immediately see the issue here. The more screens you have, the more difficult it becomes to test all possible error scenarios in every screen while performing the user flows, and the less value your test(s) will carry as much of the code in the test, is there just to take you to the screen you want to test.
It is however possible, with Espresso, to perform unit-testing of your screens — understood as testing all the functionality that is provided in that screen, in isolation .— And we’re going to focus on those screens implemented using Fragments — similar approaches could be used for Activities and/or Views.
Imagine that your application starts with a registration screen (implemented in RegistrationFragment), after which the user always faces a login screen (implemented in LoginFragment).
Imagine that you want to test different scenarios in the login screen, such as: user inputs a malformed email, wrong email, wrong email/password pair, etc.
If you were using what I called before ‘user flow’ you’d always need to get past the registration step, probably for every single test case, devaluing your test.
What you want is testing just your login screen.
There are some good solutions out there allowing to test fragments and/or views in isolation, but they all involve adding extra dependencies though, something I try to avoid, if possible.
Let’s see if we can make something very light that does not depend on anything external.
First thing, your Fragment needs a container. Let’s add to your debug build, an Activity that will serve as container for your fragments under test. And let’s call it SingleFragmentActivity.
And that’s pretty much it. You can now use this Activity in your Espresso tests. Just create a rule like the one bellow.
@RunWith(AndroidJUnit4::class)
class LoginTest {
val testActivityRule = ActivityTestRule(SingleFragmentActivity::class.java, true, true)
@Rule
fun rule() = testActivityRule
@Before
fun setUp() {
rule().activity.setFragment(LoginFragment.newInstance())
}
@Test
fun wrongEmailFormat_shouldDisableLoginButton() {
login {
email("wrong.email.com")
asssertLoginButtonDisabled()
}
}
//...
}
Note how beautiful and descriptive test can be using the DSL capabilities of Kotlin— more on this in a different (future) post :D
I you liked the article, click the 💚 below so other people will see it here on Medium.