7

I am just starting out wih junit and the first issue I'm running into is, how should I test fragments?

The activity being tested has 1 fragment which is the main layout.

@Override
protected void setUp() throws Exception {
    super.setUp();
    Intent intent = new Intent(getInstrumentation().getTargetContext(),
            ActivityWelcome.class);
    startActivity(intent, null, null);

    FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
    mFragmentWelcome = (FragmentWelcome) fragmentManager.findFragmentByTag(FragmentWelcome.TAG);
    if (mFragmentWelcome == null) {

        mFragmentWelcome= FragmentWelcome.newInstance();
        fragmentManager
                .beginTransaction()
                .replace(android.R.id.content, mFragmentWelcome, FragmentWelcome.TAG)
                .commit();
    }
}

I then go on to test the layout:

@SmallTest
public void testLayout() {

    ImageButton buttonImage = (ImageButton) getActivity().findViewById(my.package.R.id.button_invites);
    assertNotNull(buttonImage);
    assertEquals("ImageButton Invite has an invalid drawable"
            , getActivity().getResources().getDrawable(my.package.R.drawable.light_social_invite)
            , buttonImage);
}

Error:

java.lang.NullPointerException
    at my.package.test.ActivityWelcomeUnitTest.testLayout(ActivityWelcomeUnitTest.java:55)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
    at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:545)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)

it fails on:

ImageButton buttonImage = (ImageButton) mFragmentWelcome.getView().findViewById(my.package.R.id.button_invites);

I'm not sure why the fragment is null, I am obviously performing this operation incorrectly, could someone please enlighten me?

HGPB
  • 4,346
  • 8
  • 50
  • 86
  • How did you managed to run unit test. In android studio i'm getting null pointer exeption at this `Intent intent = new Intent(getInstrumentation().getTargetContext(), ActivityWelcome.class);` My testClass is extending `ActivityUnitTestCase` – Muhammad Babar Jun 04 '15 at 12:00

3 Answers3

9

Actually you have to wait for the UI thread composes the main view with the fragments.

I had the same errors and so I looked at the sources of Robotium to see how they handle that. The response is they use some methods that wait the required time.

For fragment you can use something like that before performing your assertions :

protected Fragment waitForFragment(String tag, int timeout) {
        long endTime = SystemClock.uptimeMillis() + timeout;
        while (SystemClock.uptimeMillis() <= endTime) {

            Fragment fragment = getActivity().getSupportFragmentManager().findFragmentByTag(tag);
            if (fragment != null) {
                return fragment;
            }
        }
        return null;
    }

I use a timeout of 5000ms, it seems to work well.

So in your code you should replace

mFragmentWelcome = (FragmentWelcome) fragmentManager.findFragmentByTag(FragmentWelcome.TAG);

by

mFragmentWelcome = (FragmentWelcome) waitForFragment(FragmentWelcome.TAG, 5000);

EDIT : This response assumes your junit extends ActivityInstrumentationTestCase2<T>

tbruyelle
  • 12,895
  • 9
  • 60
  • 74
  • Thanks for the reply. Makes a lot of sense, however it hasn't resolved the null pointer. – HGPB Jul 22 '13 at 14:57
  • It seems I am unable to access `getView()` – HGPB Jul 22 '13 at 15:30
  • Does your junit extend `ActivityInstrumentationTestCase2` ? Don't know if it helps but on my `setup()` I call `setActivityInitialTouchMode(false)` before all call on `getActivity()`. Also I didn't make call on `startActivity`... At last, try to increase the timeout value. – tbruyelle Jul 22 '13 at 15:35
  • Thank you. I was using `ActivityUnitTestCase` which posibly doesn't deal with fragments. – HGPB Jul 22 '13 at 15:42
  • It appears that once I had updated to `ActivityInstrumentationTestCase2` I no longer needed `waitForFragment`. I will bear this in mind going forward in case I need this sort of method in the future. – HGPB Jul 22 '13 at 17:49
  • Weird, because in my project I have to use it or I get null. – tbruyelle Jul 22 '13 at 17:51
  • 2
    Yes, but my mistake was that my `ActivityWelcome` loads the fragment by default so it already exists. Which means the creation (newInstance) block of code for the fragment is not necessary in `setUp()` (me getting mixed up). I didn't actually want to load a "seperate" fragment to test in this particular instance. Totally my misunderstanding of how `ActivityInstrumentationTestCase2` works. Thanks for your time though. Got there in the end! – HGPB Jul 22 '13 at 20:25
  • how to do this while extending `ActivityUnitTestCase` ? – Muhammad Babar Jun 05 '15 at 06:33
  • I added an additional `Thread.sleep(20)` to avoid calling findFragmentByTag too often. – wkarl Oct 04 '15 at 10:30
4

For an ActivityUnitTestCase test, I had to add the following line(s) to the waitForFragment method:

// haven't really checked if this is necessary
getInstrumentation().waitForIdleSync();
// without this, waitForFragment() failed to find the fragment
getActivity().getFragmentManager().executePendingTransactions();
ento
  • 5,801
  • 6
  • 51
  • 69
  • Thanks for clarifying this. @tbruyelle answer didn't work for me. I have checked that `waitForIdleSync()` call is not really necessary. – kazbeel Jun 04 '15 at 08:29
  • Doing `mFragment.getView()` was not null? – Muhammad Babar Jun 05 '15 at 06:41
  • using getSUPPORTFragmentManager().executePendingT... worked using ActivityUnitTestCase, because I had added the fragment by calling getSupportFragmentManager(). Hope that helps someone. – flobacca Sep 09 '15 at 02:34
  • a clarification: actually, I never got the Fragment to call onActivityCreated, but I did get the Fragment to not be null from findFragmentByTag() method. – flobacca Sep 13 '15 at 21:48
1

U can use com.robotium.solo.Solo from 'com.jayway.android.robotium:robotium-solo:5.2.1'

    mInstrumentation = InstrumentationRegistry.getInstrumentation();
    mSolo = new Solo(mInstrumentation);
    mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    .....
    mSolo.waitForFragmentById();
TeeTracker
  • 7,064
  • 8
  • 40
  • 46