简体   繁体   English

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

[英]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). 我一直在这里阅读很多链接,github和robolectric博客,但还没找到可行的解决方案(已经使用Robolectric 2.0 alpha 2)。

UPDATE: The problem also happens even if we replace SherlockFragment for android.support.v4.app.Fragment . 更新:即使我们为android.support.v4.app.Fragment替换SherlockFragment ,问题也会发生。

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: 按照这个提示后我能够测试一个SherlockFragmentActivity,但是当我将这个片段添加到我的活动xml时,这是一个SherlockFragment:

<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: 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);
    }
}

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 ps:完整的源代码可以在github上找到

Edit: Also tested with the code from square/master (2.0 alpha 3 square 5) branch and got the same problem. 编辑:还使用square / master(2.0 alpha 3 square 5)分支的代码进行测试,并得到了同样的问题。 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. 检查LayoutBuilder.constructFragment ,我认为问题是activity.getSupportFragmentManager().beginTransaction().add(fragment, tag).commit()不能使用SherlockFragment,因此fragment.getView()返回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. 我不确定我是否可以做任何事来解决这个问题......我已经在检查是否可以在robolectric上修复它,如果有人对此有任何提示,请告诉我。

You might try using Robolectric 2.0's new ActivityController, which fixes some scheduling/order-of-operations issues that often crop up. 您可以尝试使用Robolectric 2.0的新ActivityController,它修复了一些经常出现的调度/操作顺序问题。

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

It arranges for the main looper to be temporarily paused during the call to onCreate(). 在调用onCreate()期间,它会安排主循环器暂时暂停。

Calling 调用

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

will call your Fragment.onCreateView method 将调用您的Fragment.onCreateView方法

I'm not happy with this solution, but there's a way to make it work: 我对这个解决方案不满意,但有一种方法可以使它工作:

The method fragment.getView() is returning null because the onCreateView from my Fragment was never called. 方法fragment.getView()返回null,因为我的Fragment中的onCreateView从未被调用过。 It should be called by the method moveToState from android.support.v4.app.FragmentManager , but didn't because f.mFromLayout was false. 它应该由android.support.v4.app.FragmentManager moveToState方法moveToState ,但不是因为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;
    }
}

So, if you add this block of code to your fragments onCreate method, it will work. 因此,如果您将此代码块添加到片段onCreate方法中,它将起作用。

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

As I said, not a real solution, but a way to make it work before someone finds a proper solution. 正如我所说,不是一个真正的解决方案,而是一种在某人找到合适的解决方案之前使其工作的方法。

[UPDATE] This method will not work with Robolectric 2.0 Final [更新]此方法不适用于Robolectric 2.0 Final

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM