I've been looking for a way to test the UI of my Fragments separately (ie, independently from other fragments and activities) but I can't find a way to do it.
In particular, let's say I have Fragment A, Fragment B and Fragment C. The only way (app-wise) to go to Fragment C is by passing through Fragment A and Fragment B first. I am looking for a way to test Fragment C directly (potentially by mocking its dependencies, if any exists), without having to pass through Fragment A and B.
Tools I investigated so far:
monkey: only used to generate pseudo-random events through command line. Not what I want.
monkeyrunner: it can run Python programs to send event streams to my Android app, but it cannot target a particular Fragment directly with those scripts.
Espresso: white-box testing tool. This comes close to what I want, but it still requires passing through Fragment A and B before reaching Fragment C (ie, you need to start your app and then the tests will run from there).
UI Automator: black-box testing tool. This also comes close, but again, it requires passing through the previous Fragments before testing the one I want (Fragment C).
Is there any way to test the UI of a Fragment directly?
I'm am using a custom FragmentTestRule
and Espresso to test each of my Fragments
in isolation.
I have a dedicated TestActivity
that shows the tested Fragments
in my app. In my case the Activity
only exists in the debug
variant because my instrumentation tests run against debug
.
TL;DR Use the awesome FragmentTestRule library by @brais-gabin.
1. Create a TestActivity
in src/debug/java/your/package/TestActivity.java
with a content view where the tested Fragment
will be added to:
@VisibleForTesting
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout frameLayout = new FrameLayout(this);
frameLayout.setId(R.id.container);
setContentView(frameLayout);
}
}
2. Create a AndroidManifest.xml for the debug
variant and declare the TestActivity
. This is required to start the TestActivity
when testing. Add this Manifest to the debug
variant in src/debug/AndroidManifest.xml
:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity android:name="your.package.TestActivity"/>
</application>
</manifest>
3. Create the FragmentTestRule
in the androidTest
variant at src/androidTest/java/your/test/package/FragmentTestRule.java
:
public class FragmentTestRule<F extends Fragment> extends ActivityTestRule<TestActivity> {
private final Class<F> mFragmentClass;
private F mFragment;
public FragmentTestRule(final Class<F> fragmentClass) {
super(TestActivity.class, true, false);
mFragmentClass = fragmentClass;
}
@Override
protected void afterActivityLaunched() {
super.afterActivityLaunched();
getActivity().runOnUiThread(() -> {
try {
//Instantiate and insert the fragment into the container layout
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
mFragment = mFragmentClass.newInstance();
transaction.replace(R.id.container, mFragment);
transaction.commit();
} catch (InstantiationException | IllegalAccessException e) {
Assert.fail(String.format("%s: Could not insert %s into TestActivity: %s",
getClass().getSimpleName(),
mFragmentClass.getSimpleName(),
e.getMessage()));
}
});
}
public F getFragment(){
return mFragment;
}
}
4. Then you can test Fragments
in isolation:
public class MyFragmentTest {
@Rule
public FragmentTestRule<MyFragment> mFragmentTestRule = new FragmentTestRule<>(MyFragment.class);
@Test
public void fragment_can_be_instantiated() {
// Launch the activity to make the fragment visible
mFragmentTestRule.launchActivity(null);
// Then use Espresso to test the Fragment
onView(withId(R.id.an_id_in_the_fragment)).check(matches(isDisplayed()));
}
}
I developed FragmentTestRule an Andorid library using the @thaussma's idea. It allows you to test your Fragment
s in isolation.
You just need to add this:
@Rule
public FragmentTestRule<?, FragmentWithoutActivityDependency> fragmentTestRule =
FragmentTestRule.create(FragmentWithoutActivityDependency.class);
You can use Robotium.This is for android UI testing.
If you are using the Navigation Architecture component and you are using a single activity architecture in your app, you can quickly test each fragment by Deep linking to the target fragment (with appropriate arguments) at the beginning of the test.
For example:
@Rule
@JvmField
var activityRule = ActivityTestRule(MainActivity::class.java)
protected fun launchFragment(destinationId: Int,
argBundle: Bundle? = null) {
val launchFragmentIntent = buildLaunchFragmentIntent(destinationId, argBundle)
activityRule.launchActivity(launchFragmentIntent)
}
private fun buildLaunchFragmentIntent(destinationId: Int, argBundle: Bundle?): Intent =
NavDeepLinkBuilder(InstrumentationRegistry.getInstrumentation().targetContext)
.setGraph(R.navigation.navigation)
.setComponentName(MainActivity::class.java)
.setDestination(destinationId)
.setArguments(argBundle)
.createTaskStackBuilder().intents[0]
destinationId being the fragment destination id in the navigation graph. Here is an example of a call that would be done once you are ready to launch the fragment:
launchFragment(R.id.target_fragment, targetBundle())
private fun targetBundle(): Bundle? {
val bundle = Bundle()
bundle.putString(ARGUMENT_ID, "Argument needed by fragment")
return bundle
}
Doing it this way will launch the fragment directly. If your test works, then this proves that your app won't crash when the fragment is deep-linked to. It also ensures that the app will be stable if the process is killed by the system and it tries to rebuild the stack and relaunch the fragment.
来源:https://stackoverflow.com/questions/33647135/android-independent-fragment-ui-testing-tool