Robolectric:如何测试包含SherlockFragment的活动?

6
我已阅读很多有关此处、github和robolectric博客的链接,但尚未找到可行的解决方案(已使用Robolectric 2.0 alpha 2)。
更新:即使我们将SherlockFragment替换为android.support.v4.app.Fragment,问题仍然存在。
我能够在遵循此提示后测试SherlockFragmentActivity:tip,但是当我将这个作为SherlockFragment的片段添加到我的activity xml时:
<fragment 
    android:name="com.marcelopazzo.fragmentapplication.ExampleFragment" 
    android:id="@+id/example_fragment"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" />

SherlockFragment类:

public class ExampleFragment extends SherlockFragment {

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

这是由碎片膨胀的布局:

<?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>

我遇到了以下错误:

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)

这是我使用的测试类:

@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());
    }
}

应用程序在设备上运行良好。

我错过了什么?

附注:完整的源代码可在GitHub上找到。

编辑:还使用square/master(2.0 alpha 3 square 5)分支中的代码进行了测试,并遇到了同样的问题。 检查LayoutBuilder.constructFragment,我认为问题是activity.getSupportFragmentManager().beginTransaction().add(fragment, tag).commit()与SherlockFragment不兼容,因此fragment.getView()返回null。

我不确定是否可以在我的端修复此问题... 我已经在检查能否在robolectric端修复它,请让我知道是否有任何提示。

3个回答

7
你可以尝试使用 Robolectric 2.0 的新 ActivityController,它修复了一些常见的调度/操作顺序问题。
MainActivity activity = Robolectric.buildActivity(MainActivity.class)
                                    .create().get();

在调用onCreate()期间,它会安排主循环程序暂时暂停。


1
嗨@Xian,感谢回复。 我刚刚测试了2.0-rc1和2.0-rc2-SNAPSHOT。仍然出现NPE java.lang.NullPointerException: null at android.view.ViewGroup.addView(ViewGroup.java:3187) at android.view.ViewGroup.addView(ViewGroup.java:3170) at org.robolectric.res.builder.LayoutBuilder.constructFragment(LayoutBuilder.java:150) - marcelopazzo
已将更新的代码推送到 github。同时发现这与 ActionBarSherlock 无关,我在使用 android.support.v4.app.Fragment 时也遇到了相同的错误。 - marcelopazzo
问题已在Robolectric 2.1上得到修复。 - marcelopazzo

3

调用

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

将会调用您的Fragment.onCreateView方法。


谢谢您的提示。我尝试了不同的初始调用组合(参考:http://robolectric.org/activity-lifecycle/),这是适用于我的情况的最小设置: .create().visible().start().get(); 以前我只使用了 .create().visible().get(),但片段上的视图没有被创建。Robolectric 3.0。 - Alen Siljak

0

我对这个解决方案不满意,但有一种方法可以使其工作:

方法fragment.getView()返回null,因为我的Fragment的onCreateView从未被调用。它应该由android.support.v4.app.FragmentManagermoveToState方法调用,但由于f.mFromLayout为false,所以没有被调用。

f.mActivity = mActivity;
f.mFragmentManager = mActivity.mFragments;
f.mCalled = false;
f.onAttach(mActivity);
if (!f.mCalled) {
    throw new SuperNotCalledException("Fragment " + f
    + " did not call through to super.onAttach()");
}
mActivity.onAttachFragment(f);

if (!f.mRetaining) {
    f.mCalled = false;
    f.onCreate(f.mSavedFragmentState);
    if (!f.mCalled) {
        throw new SuperNotCalledException("Fragment " + f
        + " did not call through to super.onCreate()");
    }
}
f.mRetaining = false;
if (f.mFromLayout) {
    // For fragments that are part of the content view
    // layout, we need to instantiate the view immediately
    // and the inflater will take care of adding it.
    f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState),
    null, f.mSavedFragmentState);
    if (f.mView != null) {
    f.mInnerView = f.mView;
    f.mView = NoSaveStateFrameLayout.wrap(f.mView);
    if (f.mHidden) f.mView.setVisibility(View.GONE);
        f.onViewCreated(f.mView, f.mSavedFragmentState);
    } else {
        f.mInnerView = null;
    }
}

所以,如果你将这段代码添加到你的片段onCreate方法中,它就会起作用。

Field field = Fragment.class.getDeclaredField("mFromLayout");
field.setAccessible(true);
field.setBoolean(this, true);
field.setAccessible(false);

就像我说的那样,这不是一个真正的解决方案,而是在有人找到合适的解决方案之前让它工作的方法。

[更新] 这种方法将无法与Robolectric 2.0 Final一起使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接