Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Robolectric: How can I test an activity that contains a SherlockFragment?

I have been reading a lot of links from here, github and robolectric blog but couldn't find a working solution yet (already using Robolectric 2.0 alpha 2).

UPDATE: The problem also happens even if we replace SherlockFragment for android.support.v4.app.Fragment.

I was able to test a SherlockFragmentActivity after following this tip, but when I add this fragment, that is a SherlockFragment, to my activity xml:

<fragment 
    android:name="com.marcelopazzo.fragmentapplication.ExampleFragment" 
    android:id="@+id/example_fragment"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" />

The SherlockFragment class:

public class ExampleFragment extends SherlockFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

And this is the layout that is being inflated by the fragment:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/hello_again"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_again" />

</LinearLayout>

I get the following error:

java.lang.NullPointerException
    at org.robolectric.shadows.ShadowViewGroup.addView(ShadowViewGroup.java:69)
    at android.view.ViewGroup.addView(ViewGroup.java)
    at org.robolectric.res.builder.LayoutBuilder.constructFragment(LayoutBuilder.java:150)
    at org.robolectric.res.builder.LayoutBuilder.create(LayoutBuilder.java:104)
    at org.robolectric.res.builder.LayoutBuilder.doInflate(LayoutBuilder.java:42)
    at org.robolectric.res.builder.LayoutBuilder.doInflate(LayoutBuilder.java:45)
    at org.robolectric.res.builder.LayoutBuilder.inflateView(LayoutBuilder.java:62)
    at org.robolectric.shadows.ShadowLayoutInflater.inflate(ShadowLayoutInflater.java:50)
    at org.robolectric.shadows.ShadowLayoutInflater.inflate(ShadowLayoutInflater.java:55)
    at android.view.LayoutInflater.inflate(LayoutInflater.java)
    at com.squareup.test.ActionBarSherlockRobolectric.setContentView(ActionBarSherlockRobolectric.java:38)
    at com.actionbarsherlock.app.SherlockFragmentActivity.setContentView(SherlockFragmentActivity.java:262)
    at com.marcelopazzo.fragmentapplication.MainActivity.onCreate(MainActivity.java:13)
    at com.marcelopazzo.fragmentapplication.MainActivityTest.setUp(MainActivityTest.java:33)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:110)
    at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:234)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:133)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:114)
    at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:188)
    at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:166)
    at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:86)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:101)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:74)

This is the test class that I am using:

@RunWith(RobolectricTestRunner.class)
public class MainActivityTest {

    private MainActivity activity;
    private TextView textView;

    public static final String GREETINGS = "Hello world!";  

    @Before
    public void setUp() {

        ActionBarSherlock.registerImplementation(ActionBarSherlockRobolectric.class);
        ActionBarSherlock.unregisterImplementation(ActionBarSherlockNative.class);
        ActionBarSherlock.unregisterImplementation(ActionBarSherlockCompat.class);

        activity = new MainActivity();
        activity.onCreate(null);

        textView = (TextView) activity.findViewById(R.id.hello);
    }

    @Test
    public void shouldGreet() {
        assertEquals(GREETINGS, textView.getText());
    }
}

The application is working fine on the device.

What am I missing here?

ps: The full source code is available on github

Edit: Also tested with the code from square/master (2.0 alpha 3 square 5) branch and got the same problem. Checking the LayoutBuilder.constructFragment, I think the problem is that activity.getSupportFragmentManager().beginTransaction().add(fragment, tag).commit() is not working with SherlockFragment, so fragment.getView() is returning null.

I'm not sure if I can do anything on my side to fix this... I'm already checking if I can fix it on robolectric's side, please let me know if anyone have any tip on this.

like image 701
marcelopazzo Avatar asked Apr 23 '13 21:04

marcelopazzo


People also ask

Why should I use robolectric for unit testing?

If yes then, you must try Robolectric, it is a unit testing framework that allows Android applications to be tested on the JVM without an emulator or device. These are the main features of it:

How to use robolectric to search in a view?

You can also use the findViewById () to search in a view. Robolectric is not an integration test framework, i.e., you cannot not test the interaction of Android components with it. Robolectric does not require additional mocking frameworks, of course it is still possible to use frameworks like Mockito if desired.

What are the main features of robolectric?

These are the main features of it: It provides a way to run our tests inside Android Studio, without launching app on Device or Emulator. Shadow Classes, rewritten android core libraries by Robolectric, are the real magic of Robolectric. It is a replica of Android class with some exposed useful functions.

How does robolectric work with shadow classes?

Every time a method is invoked on the Android class, Robolectric first invokes the shadow class' corresponding method (if there is one). This gives the shadow classes a chance to maintain and expose extra state that wouldn't be available from just the Android classes. Let's assume our MainActivity has a Button that launches a SecondActivity.


2 Answers

You might try using Robolectric 2.0's new ActivityController, which fixes some scheduling/order-of-operations issues that often crop up.

MainActivity activity = Robolectric.buildActivity(MainActivity.class)
                                    .create().get();

It arranges for the main looper to be temporarily paused during the call to onCreate().

like image 168
Xian Avatar answered Sep 30 '22 20:09

Xian


Calling

Robolectric.buildActivity(YourActivityClass.class).attach().create().start().resume().get();

will call your Fragment.onCreateView method

like image 26
ffgiraldez Avatar answered Sep 30 '22 20:09

ffgiraldez