21

I have a Fragment that I want to test. I created a test Activity to which I add this Fragment and run some Espresso tests.

However, Espresso does not find any of the views inside the Fragment. It dumps the view hierarchy and it is all empty.

I do not want to embed the Fragment in a test Activity. I want to just test the Fragment in isolation. Has anyone done this? Is there a sample that has similar code?

@RunWith(AndroidJUnit4.class)
class MyFragmentTest {

    @Rule
    public ActivityTestRule activityRule = new ActivityTestRule<>(
        TestActivity.class
    );

    @Test
    public void testView() {
        MyFragment myFragment = startMyFragment();
        myFragment.onEvent(new MyEvent());
        // MyFragment has a RecyclerView
        // onEvent is an EventBus callback that contains no data in this test
        // I want the Fragment to display an empty list text and hide the RecyclerView

        onView(withId(R.id.my_empty_text)).check(matches(isDisplayed()));
        onView(withId(R.id.my_recycler)).check(doesNotExist()));
    }

    private MyFragment startMyFragment() {
        FragmentActivity activity = (FragmentActivity) activityRule.getActivity();
        FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
        MyFragment myFragment = new MyFragment();
        transaction.add(myFragment, "myfrag");
        transaction.commit();
        return myFragment;
    }
}
Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
greenrobo
  • 781
  • 1
  • 6
  • 9
  • 1
    Did you try doing any web searches for this? I find lots of examples. – Doug Stevenson Feb 18 '16 at 02:05
  • It's always a good idea to show us your code. – Emzor Feb 18 '16 at 02:05
  • 1
    I tried to search on the net. I have not been able to find any examples. @DougStevenson, I would appreciate it if you could share what you found. – greenrobo Feb 18 '16 at 02:48
  • Because of the proprietary nature of the app, I am not allowed to freely share the code. But I will try to create a sample. – greenrobo Feb 18 '16 at 02:49
  • I did that. However, all the samples that I find use the ACTUAL parent activity to test a fragment. None of the samples isolate the fragment and test it stand-alone. – greenrobo Feb 18 '16 at 03:15

5 Answers5

7

I will do in following way Create a ViewAction as follows:

public static ViewAction doTaskInUIThread(final Runnable r) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return null;
        }

        @Override
        public void perform(UiController uiController, View view) {
            r.run();
        }
    };
}

Then use below to launch code which should be run in UI Thread

onView(isRoot()).perform(doTaskInUIThread(new Runnable() {
        @Override
        public void run() {
            //Code to add your fragment or anytask that you want to do from UI Thread
        }
    }));

below is an example of test case adding fragment view hierarchy

    @Test
public void testSelectionOfTagsAndOpenOtherPage() throws Exception{

    Runnable r = new Runnable() {
        @Override
        public void run() {
            //Task that need to be done in UI Thread (below I am adding a fragment)

        }
    };
    onView(isRoot()).perform(doTaskInUIThread(r));

}
Ankur
  • 429
  • 4
  • 10
6
public class VoiceFullScreenTest {
    @Rule
    public ActivityTestRule activityRule = new ActivityTestRule<>(
            TestActivity.class);

    @Test
    public void fragment_can_be_instantiated() {
        activityRule.getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                VoiceFragment voiceFragment = startVoiceFragment();
            }
        });
        // Then use Espresso to test the Fragment
        onView(withId(R.id.iv_record_image)).check(matches(isDisplayed()));
    }

    private VoiceFragment startVoiceFragment() {
        TestActivity activity = (TestActivity) activityRule.getActivity();
        FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
        VoiceFragment voiceFragment = new VoiceFragment();
        transaction.add(voiceFragment, "voiceFragment");
        transaction.commit();
        return voiceFragment;
    }


}

You can start your fragment from UI thread as mentioned above.

Siddharth Yadav
  • 161
  • 1
  • 6
5

You can use the androidx.fragment:fragment-testing library. Launching the fragment in your test method is as simple as:

val fragmentArgs = Bundle()
androidx.fragment.app.testing.launchFragmentInContainer<MyFragment>(fragmentArgs)

You can find more information about this library in the Test your fragments Android Developers' guide.

Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
4

You can use FragmentTestRule.

Instead of the regular ActivityTestRule you must use:

@Rule
public FragmentTestRule<?, FragmentWithoutActivityDependency> fragmentTestRule =
    FragmentTestRule.create(FragmentWithoutActivityDependency.class);

You can find more details in this blog post.

Brais Gabin
  • 5,827
  • 6
  • 57
  • 92
1

You've probably forgot to inject the fragment in the view hierarchy. Try defining the holder container for your fragment in the TestActivity layout (like a FrameLayout with id fragment_container) and then instead of just add(myFragment, "tag"), use the add(R.id.fragment_container, myFragment, "tag") (this method). I guess you could use the replace method with the same signature as well.

karlicoss
  • 2,501
  • 4
  • 25
  • 29
  • I tried this method (using add(id, fragment, tag)) but that did not help. I suspect that this is because of multiple threads interacting. Espresso somehow does not like this. – greenrobo Feb 18 '16 at 21:54