Android:为Fragments编写测试用例

31
在我的以前的项目中,我通过Activities完成大部分工作,并按照文档使用ActivityInstrumentationTestCase2:
http://developer.android.com/tools/testing/activity_testing.html
我知道如何处理Activity测试用例,但是当涉及到Fragment时,我没有太多的想法,也没有找到相关文档。那么,在拥有一个或两个活动的几个片段时,如何编写测试用例呢?任何示例代码或样例都将更有帮助。

也许你应该添加另一个标签,比如“android-testing”,以便引起更多关注。除此之外,Activity 就像 Fragment 一样,只不过它有 onCreateView() 方法。而且,即使应用程序处于活动状态,Fragment 也可以从内存中释放。 - The Original Android
@TheOriginalAndroid 已添加 :-) - AnswerDroid
你可以看一下MVP方法,它会让你的测试变得更容易。 - Dmitry Zaytsev
@DmitryZaitsev,您能再详细解释一下吗?或者提供一个示例或链接。 - AnswerDroid
http://antonioleiva.com/mvp-android/ - Dmitry Zaytsev
5个回答

27

以下是使用ActivityInstrumentationTestCase2的初步指南:

第一步。创建一个空白的 Activity 来容纳您的碎片(s)

  private static class FragmentUtilActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      LinearLayout view = new LinearLayout(this);
      view.setId(1);

      setContentView(view);
    }
  }

步骤 2: 在你的测试中,实例化你的碎片并将其添加到空白活动中

public class MyFragmentTest extends ActivityInstrumentationTestCase2<FragmentUtilActivity> { 
    private MyFragment fragment;

    @Before
    public void setup() {
        fragment = new MyFragment();
        getActivity().getFragmentManager().beginTransaction().add(1, fragment, null).commit();
    }
}

第三步 测试您实例化的片段

@Test
public void aTest() {
    fragment.getView().findViewById(...);
}
如果您正在使用robolectric,那么使用FragmentUtilTest类非常简单:
@Test
public void aTest() {
    // instantiate your fragment
    MyFragment fragment = new MyFragment();

    // Add it to a blank activity
    FragmentTestUtil.startVisibleFragment(fragment);

    // ... call getView().findViewById() on your fragment
}

在Android Studio中,它会自动创建一个测试包。但是它不允许我在该包内添加活动。有什么想法吗? - AnswerDroid
2
要测试support-v4 Fragments,请使用SupportFragmentTestUtil.startVisibleFragment() - ThomasW
当片段依赖于其活动并且活动本身从分页器运行片段时,如何处理? - Siavash Abdoli
@SiavashA:这取决于你想要测试什么。如果你只想测试片段,应该将所有功能封装在片段中,并模拟/存根任何外部依赖项。或者,如果 Activity 与 Fragment 太紧密耦合,也可以直接编写针对 Activity 的测试。如果您有更具体的问题,请随时编写新问题。 - Nachi
@AnswerDroid,Activity不需要添加在“test”包中,而是只需在“java”包中添加。因为activity类名中没有Test关键字。 - Adi

4
这是我的工作方案:
  1. Create an instrumentation unit test class for this in androidTest directory, i.e.:

    public class FragmentTest extends 
    ActivityInstrumentationTestCase2<MainActivity> {
         private MainActivity testingActivity;
         private TestFragment testFragment;
         //... 
    }
    
  2. call this constructor inside this new class:

    public FragmentTest() {
    
        super(MainActivity.class);
    }
    
  3. override the setUp() method (be sure to have R.id.fragmentContainer in your Activity class) where you will call at the end waitForIdleSync():

    @Override
    protected void setUp() throws Exception {
        super.setUp();
    
        // Starts the activity under test using
        // the default Intent with:
        // action = {@link Intent#ACTION_MAIN}
        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
        // All other fields are null or empty.
        testingActivity = getActivity();
    
        testFragment = new TestFragment();
        testingActivity.getFragmentManager().beginTransaction().add(R.id.fragmentContainer,testFragment,null).commit();
        /**
         * Synchronously wait for the application to be idle.  Can not be called
         * from the main application thread -- use {@link #start} to execute
         * instrumentation in its own thread.
         *
         * Without waitForIdleSync(); our test would have nulls in fragment references.
         */
        getInstrumentation().waitForIdleSync();
    }
    
  4. Write a test method, for example somethng like:

    public void testGameFragmentsTextViews() {
    
       String empty = "";
       TextView textView = (TextView)testFragment.getView().findViewById(R.id.myTextView);
       assertTrue("Empty stuff",(textView.getText().equals(empty)));   
    }
    
  5. Run the test.


如何使用仪器测试执行该操作,以针对片段进行 onView(withId(R.id.txt_first_num_add)).perform(typeText("6")); - Lassie
getInstrumentation().waitForIdleSync(); 对我很有帮助。在我的情况下,我传递了参数和一切...但它没有初始化viewmodel...但这个案例对我很有帮助。 - Reshma

2
使用您的主活动作为发送到ActivityInstrumentationTestCase2的测试活动,然后您可以通过启动片段的主要活动的片段管理器来处理片段。这甚至比拥有测试活动更好,因为它使用您在主活动中编写的逻辑来测试场景,从而提供更完整和全面的测试。
示例:
public class YourFragmentTest extends ActivityInstrumentationTestCase2<MainActivity> {  
    public YourFragmentTest(){
            super(MainActivity.class);
        }

}

这看起来很有趣。我会去检查一下并告诉你。 - AnswerDroid
getActivity().getSupportFragmentManager().beginTransaction() .replace(R.id.fragment, new LoginFragment(), "LOGIN") .commit();我正在执行这个操作。然后我想测试LoginFragment。如何实现? - AnswerDroid
你能提供更多细节吗?你想测试什么?这是你应该进行基本单元测试的地方 - 在这里查看:http://developer.android.com/tools/testing/what_to_test.html 以获取更多关于测试内容的想法。 - melkoth

2

AndroidX 提供了一个库,FragmentScenario,用于创建片段并更改它们的状态。

app/build.gradle

dependencies {
    def fragment_version = "1.0.0"
    // ...
    debugImplementation 'androidx.fragment:fragment-testing:$fragment_version'
}

例子

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" and "factory" arguments are optional.
        val fragmentArgs = Bundle().apply {
            putInt("selectedListItem", 0)
        }
        val factory = MyFragmentFactory()
        val scenario = launchFragmentInContainer<MyFragment>(
                fragmentArgs, factory)
        onView(withId(R.id.text)).check(matches(withText("Hello World!")))
    }
}

更多内容请查看官方文档


你试过这个@Levon Petrosyan吗?虽然如此,我已经接受了这个答案。 - AnswerDroid

0

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