31

In my previous projects I've done most of the work through Activities and used ActivityInstrumentationTestCase2 as per the document:
http://developer.android.com/tools/testing/activity_testing.html
I have an idea how to work with Activity Test cases; but when it comes to Fragment ,I don't have much idea nor found much documents related to that. So how to write test cases when I have several fragments with one or two actvities? Any example code or sample would be more helpful.

AnswerDroid
  • 1,873
  • 2
  • 33
  • 52

5 Answers5

27

Here's a rough guide using ActivityInstrumentationTestCase2:

Step 1. Create a blank Activity to hold your fragment(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);
    }
  }

Step 2: Inside your test, instantiate your fragment and add it to the blank activity

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

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

Step 3 Test your instantiated fragment

@Test
public void aTest() {
    fragment.getView().findViewById(...);
}

If you're using robolectric, this is pretty straightforward using the FragmentUtilTest class:

@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
}
Nachi
  • 4,218
  • 2
  • 37
  • 58
  • In android studio ; it automatically creates a test package.However it is not allowing me to add an activity inside that package. Any idea? – AnswerDroid Jun 22 '15 at 12:04
  • 2
    To test support-v4 Fragments, use `SupportFragmentTestUtil.startVisibleFragment()`. – ThomasW Jul 07 '16 at 06:19
  • how to handle this when fragment depends on its activity and the activity itself run fragment from pager? – Siavash Abdoli Jul 23 '16 at 07:54
  • @SiavashA: this depends on what you are trying to test. If you want to test just the fragment, you should encapsulate all functionality within the fragment and mock/stub any external dependencies. Alternatively you can also directly write tests for your Activity if it is too closely-coupled with the Fragment. Feel free to write a new question if you have more specific questions. – Nachi Jul 25 '16 at 13:28
  • @AnswerDroid Activity does not to be added inside the `test` package , you have to add in `java` package only ,As activity classname doesn't have **Test** keyword in it . – Adi Oct 02 '18 at 12:54
4

Here is my working solution:

  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.

Michał Dobi Dobrzański
  • 1,449
  • 1
  • 20
  • 19
  • How to perform this by using instrumentation test for fragment onView(withId(R.id.txt_first_num_add)) .perform(typeText("6")); – Lassie Oct 27 '17 at 13:20
  • getInstrumentation().waitForIdleSync(); helped me. In my case,i was passing argument and everything.. but it was not initializing viewmodel.. but this case helped me . – Reshma Nov 02 '22 at 13:30
2

Use your main activity as the test activity that you send to ActivityInstrumentationTestCase2. Then you can work with the fragments through the fragment manager of your main activity that launches the fragments. This is even better than having a test activity because it uses the logic that you write in your main activity to test scenarios, which gives a fuller and more complete test.

Example:

public class YourFragmentTest extends ActivityInstrumentationTestCase2<MainActivity> {  
    public YourFragmentTest(){
            super(MainActivity.class);
        }

}
melkoth
  • 178
  • 7
  • this looks interesting.. I will check it and let you know – AnswerDroid Jun 26 '15 at 03:49
  • getActivity().getSupportFragmentManager().beginTransaction(). replace(R.id.fragment,new LoginFragment(),"LOGIN") .commit(); I am doing this . .Then I wish to test LoginFragment.How to acheive? – AnswerDroid Jun 26 '15 at 06:26
  • Can you provide more details? What are you trying to test? This is where you should be doing your basic unit testing - look here: [http://developer.android.com/tools/testing/what_to_test.html](http://developer.android.com/tools/testing/what_to_test.html) for more ideas of what to test. – melkoth Jun 26 '15 at 14:05
2

AndroidX provides a library, FragmentScenario, to create fragments and change their state.

app/build.gradle

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

example

@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!")))
    }
}

More at official docs.

Levon Petrosyan
  • 8,815
  • 8
  • 54
  • 65
0

Right now ActivityInstrumentationTestCase2 is deprecated. Now you can use rules in order to use activities within your tests: http://wiebe-elsinga.com/blog/whats-new-in-android-testing/

In order for those to work you'll have to add the dependencies to you build.gradle:

testCompile 'com.android.support.test:rules:0.5'
testCompile 'com.android.support.test:runner:0.5'

(See Why cannot I import AndroidJUnit4 and ActivityTestRule into my unit test class?)

saitho
  • 899
  • 9
  • 17