Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - Independent Fragment UI testing tool

Tags:

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?

like image 871
Bitcoin Cash - ADA enthusiast Avatar asked Nov 11 '15 08:11

Bitcoin Cash - ADA enthusiast


People also ask

What tool is used for UI testing in Android Studio?

The Espresso Test Recorder tool lets you create UI tests for your app without writing any test code. By recording a test scenario, you can record your interactions with a device and add assertions to verify UI elements in particular snapshots of your app.

What is AndroidX test?

AndroidX Test is a collection of Jetpack libraries that lets you run tests against Android apps. It also provides a series of tools to help you write these tests. For example, AndroidX Test provides JUnit4 rules to start activities and interact with them in JUnit4 tests.

How can I tell if an Android fragment is loaded?

You should be using FragmentManager 's findFragmentById() method, then you can check which fragment it is by using instanceof . Your code should look something like this: mFragmentManager = getSupportFragmentManager(); Fragment frag = mFragmentManager. findFragmentById(R.


1 Answers

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()));
    }
}
like image 86
thaussma Avatar answered Sep 21 '22 10:09

thaussma