【问题标题】:Robolectric: How can I test an activity that contains a SherlockFragment?Robolectric:如何测试包含 SherlockFragment 的活动?
【发布时间】:2013-04-23 21:42:36
【问题描述】:

我从这里、github 和 robolectric 博客中阅读了很多链接,但还没有找到可行的解决方案(已经使用 Robolectric 2.0 alpha 2)。

更新:即使我们将SherlockFragment 替换为android.support.v4.app.Fragment,也会出现问题。

在关注此tip 之后,我能够测试 SherlockFragmentActivity,但是当我将此片段(即 SherlockFragment)添加到我的活动 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());
    }
}

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

我在这里错过了什么?

ps:完整的源代码可在github获得

编辑:还使用来自 square/master (2.0 alpha 3 square 5) 分支的代码进行了测试,并遇到了同样的问题。 检查LayoutBuilder.constructFragment,我认为问题在于activity.getSupportFragmentManager().beginTransaction().add(fragment, tag).commit() 不适用于SherlockFragment,所以fragment.getView() 返回null。

我不确定我是否可以做任何事情来解决这个问题......我已经在检查我是否可以在 robolectric 方面解决它,如果有人对此有任何提示,请告诉我。

【问题讨论】:

    标签: android android-fragments actionbarsherlock robolectric


    【解决方案1】:

    您可以尝试使用 Robolectric 2.0 的新 ActivityController,它修复了一些经常出现的调度/操作顺序问题。

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

    它安排主循环器在调用 onCreate() 期间暂时暂停。

    【讨论】:

    • 嗨@Xian,感谢您的回复。我刚刚用 2.0-rc1 和 2.0-rc2-SNAPSHOT 进行了测试。仍然在 org.robolectric.res.builder 的 android.view.ViewGroup.addView(ViewGroup.java:3170) 的 android.view.ViewGroup.addView(ViewGroup.java:3187) 处获得 NPE java.lang.NullPointerException: null。 LayoutBuilder.constructFragment(LayoutBuilder.java:150)
    • 将更新后的代码推送到github。还发现这与 ActionBarSherlock 无关,我在使用 android.support.v4.app.Fragment 时遇到了同样的错误。
    • 问题已在 robolectric 2.1 上得到修复
    【解决方案2】:

    打电话

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

    将调用您的 Fragment.onCreateView 方法

    【讨论】:

    • 感谢您的提示。我尝试了初始调用的不同组合(参考:robolectric.org/activity-lifecycle),这是适用于我的案例的最小设置:.create().visible().start().get();以前我只使用 .create().visible().get() 并且没有创建片段上的视图。 Robolectric 3.0。
    【解决方案3】:

    我对这个解决方案不满意,但有办法让它发挥作用:

    fragment.getView() 方法返回 null,因为我的 Fragment 中的 onCreateView 从未被调用过。它应该由android.support.v4.app.FragmentManager 中的moveToState 方法调用,但没有因为f.mFromLayout 为假。

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多