59

I know there is a Robolectric.shadowOf(Fragment) method and a ShadowFragment class, thought they aren't listed on the docs, but I can't make it work.

myFragment = new MyFragment();
myFragment.onCreateView(LayoutInflater.from(activity), (ViewGroup) activity.findViewById(R.id.container), null);
myFragment.onAttach(activity);
myFragment.onActivityCreated(null); 

I'm working with API level 13 (Honeycomb).

Thanks.

Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
kaneda
  • 5,981
  • 8
  • 48
  • 73

7 Answers7

114

Edit #4 & #5: In Robolectric 3.*, they split up the fragment starting functions.

For support fragments, you will need to add a dependency to your build.gradle:

testCompile "org.robolectric:shadows-supportv4:3.8"

Import: org.robolectric.shadows.support.v4.SupportFragmentTestUtil.startFragment;

For platform fragments, you don't need this dependency. Import: import static org.robolectric.util.FragmentTestUtil.startFragment;

They both use the same name of startFragment().

import static org.robolectric.shadows.support.v4.SupportFragmentTestUtil.startFragment;

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class YourFragmentTest
{
    @Test
    public void shouldNotBeNull() throws Exception
    {
        YourFragment fragment = YourFragment.newInstance();
        startFragment( fragment );
        assertNotNull( fragment );
    }
}

Edit #3: Robolectric 2.4 has an API for support and regular fragments. You can either use the newInstance() pattern or use the constructor when constructing your Fragment's.

import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertNotNull;
import static org.robolectric.util.FragmentTestUtil.startFragment;

@RunWith(RobolectricGradleTestRunner.class)
public class YourFragmentTest
{
    @Test
    public void shouldNotBeNull() throws Exception
    {
        YourFragment fragment = new YourFragment();
        startFragment( fragment );
        assertNotNull( fragment );
    }
}

Edit #2: There's a new helper if you're using support fragments (one that supports regular activities/fragments should be in the next release):

import static org.robolectric.util.FragmentTestUtil.startFragment;

@Before
public void setUp() throws Exception
{
    fragment = YourFragment.newInstance();
    startFragment( fragment );
}

Edit: If you upgraded to Robolectric 2.0:

public static void startFragment( Fragment fragment )
{
    FragmentActivity activity = Robolectric.buildActivity( FragmentActivity.class )
                                           .create()
                                           .start()
                                           .resume()
                                           .get();

    FragmentManager fragmentManager = activity.getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.add( fragment, null );
    fragmentTransaction.commit();
}

Original answer

As the other commenter suggested, you do need to use the fragment manager (instead of calling the lifecycle methods you listed above).

@RunWith(MyTestRunner.class)
public class YourFragmentTest
{
    @Test
    public void shouldNotBeNull() throws Exception
    {
        YourFragment yourFragment = new YourFragment();
        startFragment( yourFragment );
        assertNotNull( yourFragment );
    }

I create a test runner and have a function that starts up a fragment for me so I can use it everywhere.

public class MyTestRunner extends RobolectricTestRunner
{
    public MyTestRunner( Class<?> testClass ) throws InitializationError
    {
        super( testClass );
    }

    public static void startFragment( Fragment fragment )
    {
        FragmentManager fragmentManager = new FragmentActivity().getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add( fragment, null );
        fragmentTransaction.commit();
    }
}
Thomas Keller
  • 5,933
  • 6
  • 48
  • 80
colabug
  • 2,602
  • 2
  • 22
  • 28
  • 1
    Can you prove the above actually calls the fragment's lifecycle functions such as onCreateView? After this my fragment is not null but members in my fragment that require onCreateView are still are. – brk3 Oct 17 '12 at 14:37
  • I'd use the debugger to verify that lifecycle methods were called. Perhaps there is another issue with the fragment. Check out this example for testing view elements in fragments: https://github.com/colabug/Animation/blob/master/src/test/java/com/colabug/NewFragmentTest.java (main project: https://github.com/colabug/Animation/) – colabug Oct 17 '12 at 15:19
  • Thanks for the example, played around with your project and confirmed onCreateView is indeed being called so it must be something else wrong with my code as you say. – brk3 Oct 17 '12 at 16:22
  • Given that startFragment(...) above is static and refers to no fields, it would seem to be more something you shove in a TestUtils class than a TestRunner..? – android.weasel Nov 04 '12 at 08:30
  • @android.weasel Not opposed to a separate utils class, but it seemed like overkill for this answer. – colabug Nov 13 '12 at 01:45
  • @android.weasel Also, typically you'd add more functions to the test runner. Check this out for more information: http://blog.chariotsolutions.com/2012/08/android-unit-testing-with-robolectric.html – colabug Nov 13 '12 at 01:52
  • @colabug This example's MyTestRunner class is more the overkill - it has no functionality and degrades to a plain FragmentTestUtils class, leaving the test to RobolectricTestRunner. While a real-world MyTestRunner would bind in some new shadows, cluttering it with vaguely related statics risks semantic confusion: A test under one TestRunner might need to invoke a different TestRunner's static methods - not a problem if the statics are in TestUtility classes. It's not as if new classes are expensive, or that over-stuffing MyTestRunner saves anything in terms of (inexpensive) import statements. – android.weasel Nov 16 '12 at 16:25
  • @colabug By the way, useful article (aside from our difference about where to keep statics). – android.weasel Nov 16 '12 at 16:27
  • @android.weasel You're arguing a petty detail. It solved the question and works. Glad you enjoyed my article about testing. – colabug Nov 16 '12 at 18:02
  • This approach doesn't work with Robolectric 2.x :) Did you migrate already? – Eugen Martynov Jun 04 '13 at 05:11
  • @EugenMartynov I haven't migrated. Do you have a solution you can share for the new way of doing things? – colabug Jun 11 '13 at 19:43
  • @colabug don't have it yet. We didn't migrate also :) – Eugen Martynov Jun 12 '13 at 08:21
  • @EugenMartynov - Figured it out yesterday and updated the answer. – colabug Jun 13 '13 at 14:08
  • @colabug, nice! Thank you for sharing, but for me it still doesn't work: android.view.InflateException: XML file jar:/Users/eugen/.m2/repository/org/robolectric/android-res/4.1.2_r1_rc/android-res-4.1.2_r1_rc-real.jar!/res/layout/preference_list_fragment.xml line #-1 (sorry, not yet implemented): Error inflating class Time to contribute a little :) – Eugen Martynov Jun 13 '13 at 20:09
  • @EugenMartynov I just finished debugging the same error message when I was passing a bundle to the Fragment. Make sure to try it with a vanilla/new/empty fragment to make sure it's not something weird with your fragment. – colabug Jun 13 '13 at 20:17
  • 1
    startFragment seems to be available for robolectric 2.x in org.robolectric.util.FragmentTestUtil – Somatik Sep 18 '13 at 13:51
  • @Somatik - Thanks for the heads up, I edited my answer above to include this information. Note: It's only for the support version. – colabug May 15 '14 at 13:05
  • In case of using `fragmentTransaction.commit();` inside test you should also call `fragmentManager.executePendingTransactions();` becaouse commit is executed asynchronously which cause a problem inside test environment. – Falcon Aug 13 '21 at 08:12
37

You guys are all doing this the hard way. Just use FragmentTestUtil.

FragmentTestUtil.startFragment(yourfragment);
Brian Griffey
  • 4,751
  • 1
  • 25
  • 26
  • 1
    This is a relatively new call, and it only works if you are using the support library fragments, FWIW. –  Jan 23 '14 at 21:11
  • Looks like they extended the class to both support and non-support Fragments (https://github.com/robolectric/robolectric/blob/master/src/main/java/org/robolectric/util/FragmentTestUtil.java) – Maragues Feb 04 '14 at 09:29
  • 7
    If you want to test things that require the `Fragment` to be visible, you'll need to call `ActivityController.of(yourFragment.getActivity()).visible()`. This got me into trouble when trying to verify that my `Fragment`'s `onCreateOptionsMenu` was getting called correctly. Adding the call to `visible()` on the parent activity got it working. – user1978019 Apr 27 '14 at 06:11
  • 2
    Or you could directly user `FragmentTestUtil.startVisibleFragment()` – Jaydeep Solanki Jan 14 '15 at 04:28
  • This has changed in 3.0. See my answer above with updated information. – colabug Jan 20 '16 at 13:35
  • 2
    Robolectric.buildFragment() is the up to date version – narancs Feb 13 '18 at 02:04
  • is there any way of getting a shadow of the fragment's activity in this case? The obvious `shadowOf(fragment.activity)` does not work – Huw Davies Jul 17 '18 at 07:57
15

Support fragments have been moved to module:

shadows-support-v4

(as of July,2015, Robolectric v3.0)

Add a gradle dependency to app/build.gradle:

testCompile 'org.robolectric:shadows-support-v4:3.0'

Then import to your Robolectric test java class:

import org.robolectric.shadows.support.v4.SupportFragmentTestUtil;

Then you can start & use a support-v4 fragment for testing:

@Test
public void minimalFragmentTest() throws Exception {
    MyFunFragment fragment = new MyFunFragment();
    SupportFragmentTestUtil.startVisibleFragment(fragment);
    assertThat(fragment.getView()).isNotNull();
}

References:

Baker
  • 24,730
  • 11
  • 100
  • 106
4

Old android fragments are already deprecated, seems like support fragments soon will be deprecated too. To test androidx fragments you can use fragment scenarios with robolectric https://developer.android.com/training/basics/fragments/testing

testImplementation 'androidx.fragment:fragment-testing:1.2.2'
val scenario = launchFragmentInContainer<MyFragment>()
scenario.onFragment { fragment ->
    assertNotNull(fragment.view.synteticInflatedView)
}
Link182
  • 733
  • 6
  • 15
2

I'm pretty sure you have to create a FragmentTransaction using the FragmentManager, then it will work.

Christopher Perry
  • 38,891
  • 43
  • 145
  • 187
2

I just wanted to add that in Robolectric 2.0 even after doing:

activity = Robolectric.buildActivity(FragmentActivity.class).create().start().resume().get();
fragment.show(activity.getSupportFragmentManager(), null);
fragment.getDialog();  //This stills returns null

It still returned null for me. what I did was to add activity.getSupportFragmentManager().executePendingTransaction(); and it worked.

It seems robolectric doesn't run this for some reason. it seems that maybe the Looper is paused or something. any way this worked for me and it looks like this:

activity = Robolectric.buildActivity(FragmentActivity.class).create().start().resume().get();
fragment.show(activity.getSupportFragmentManager(), null);
activity.getSupportFragmentManager().executePendingTransactions();
fragment.getDialog();
Maragues
  • 37,861
  • 14
  • 95
  • 96
superjugy
  • 301
  • 4
  • 12
1
SupportFragmentTestUtil.startFragment(fragment, AppCompatActivity::class.java)

If the activity is extending AppCompatActivity

This is using Kotlin

prijupaul
  • 2,076
  • 2
  • 15
  • 17