How to set Unit Test to Fragment in Android

Development issue/problem:

I would like to test the Android fragment class.

Can I set up a test with AndroidTestCase or should I use ApplicationTestCase?

Are there any useful examples of how these two TestCases can be used? The testing examples on the developer’s website are minimal and it seems easy to focus on the testing activities.

All I’ve found elsewhere are examples where the AndroidTestCase class is extended, but then it just checks that both numbers are added, or if context is used, it just does a simple Get and checks that something is non-zero!

From what I understand, the fragment should live in the activity. So, can I create a simulated activity or get an application or context to provide an activity in which I can test my extract?

Should I create my own ActivityUnitTestCase and then use the ActivityUnitTestCase?

How can I solve this problem?

Solution 1:

I have been struggling with the same question. Especially since most of the code samples are already outdated + Android Studio/SDK is improving, so sometimes the old answers are no longer relevant.

So, the first thing to do: Decide whether you want to use instrumental or simple JUnit tests.

The difference between the two is well described here by S.D.;
in brief: JUnit testing is simpler and does not require the use of an emulator, instrumental – to give you the best possible experience with the real device (sensors, gps, interaction with other applications, etc.) Learn more about testing on Android.

1. JUnit fragmentation test

Assume that you do not need heavy instrumental testing and that simple daytime testing is sufficient.
For this I use the beautiful Roboelectric frame.

Place the gradle:

Dependencies {
…..
testCompile ‘junit:junit:4.12’
testCompile ‘org.robolectric:robolectric:3.0’
testCompile org.mockito:mockito-core:1.10.8
testCompile (‘com.squareup.assertj:assertj-android:1.0.0’) {
exclude module : Support notes
}
…..
}

Mockito, AsserJ are optional, but I found them very useful, so I recommend including them.

Then specify unit tests as a test artifact in the build variants:
enter the image description here .

Now it’s time to write real tests
Let’s take the standard example of the Empty Fragment project.

I added a few lines of code to revise something:

import android.os.bundle;
import android.support.v4.app.fragment;
import android.view.LayoutInflater;
import android.view.view;
import android.view.viewGroup;
import java.util.list;
import java.util.list ;

the MainActivityFragment public class extends the fragment {

List of private cows;
public MainActivityFragment() {}

@All
public view onCreateView(inflatable screen, ViewGroup container,
bundle savedStatus) {
cows = new ArrayList();
cows.add(new Cow(Burka, 10));
cows.add(new Cow(Zorka, 9));
cows.add(new Cow(Kruzenshtern, 15)) ;

returnblower.inflate.inflate(R.layout.fragment_main, container, false);
}

int computeYoungCows(int maxAge) {
if (cows ==zero) {
throw new IllegalStateException(onCreateView not called);
}

if (getActivity() ==zero) {
throw new IllegalStateException(Activity is null);
}

if (getView() == null) {
throw new IllegalStateException(View is null);
}

int result = 0;
if (cow.age <= maxAge) {
if (cow.age <= maxAge) {
result++;
}
}

Reverse the result;
}
}

And a big cow:

public class Cow {
public string name ;
public intleeft age ;

public cow(String name, age int) {
ce.nom = name;
ce.âge = age;
}
}

A robotic test kit would look like this:

import android.app.Application;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.test.ApplicationTestCase ;

import junit.framework.assert;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunMet;
import org.robolectric.Robolectric;
import org.robolectricGradleTestRunner;
import org.robolectric.annotation.Config ;

import static org.assertj.core.api.assertions.assertDat;
import static org.mockito.mockito;
import static org.mockito.Mockito.when ;

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk=21)
public class MainActivityFragmentTest extends ApplicationTestCase {

public MainActivityFragmentTest() {
super(Application.class);
}

Main activity Main activity;
Main activity Main activity Main activity

For
public void setUp() {
mainActivity = Robolectric.setupActivity.class);
mainActivityFragment = new MainActivityFragment();
startFragment(mainActivityFragment);
}.

@Test
public invalid testMainActivity() {
Assert.assertNotNull(mainActivity);
}

@Test
public invalid testCowsCounter() {
assertionThat(mainActivityFragment.calculateYoungCows(10)).isEqualTo(2);
assertionThat(mainActivityFragment.calculateYoungCows(99)).isEqualTo(3);
}

private void startFragment( fragment ) {
FragmentManager fragmentManager = mainActivity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.startTransaction();
fragmentTransaction.add(fragment, null);
fragmentTransaction.commit();
}
}

That is, we create an activity via Robolectric.setupActivity, a new extract from the setUp() test classes. Optionally, you can run the fragment directly from setUp() or directly from the test.

NOTE! I haven’t spent much time with it, but it seems almost impossible to link it to Dagger (I don’t know if it’s easier with Dagger2), because you can’t set up a custom test application with mock injections.

2. Instrumental examination of fragments

The complexity of this approach is highly dependent on whether or not the dagger/addiction injection technique is used in the application being tested.

In the build options, specify Android tool tests as the test artifact:
enter the image description here .

In Cradle, I add these addictions:

Dependencies {
…..
androidTestCompile com.google.dexmaker:1.1
androidTestCompile com.google.dexmaker:dexmaker-mockito:1.1
androidTestCompile ‘com.squareup.assertj:assertj-android:1.0.0’
androidTestCompile org.mockito:mockito-core:1.10.8
}
…..
}

(Again, almost all are optional, but they can make your life easier).

– If you don’t have a dagger….

It’s a happy medium. The difference with Robolelectric would only be visible in small details.

Preliminary step 1 : If you want to use Mockito, you need to run it on devices and emulators with this hack:

TestUtils public class {
private static final String CACHE_DIRECTORY = /data/data/ + BuildConfig.APPLICATION_ID + /cache ;
public static final String DEXMAKER_CACHE_PROPERTY = dexmaker.dexcache ;

public static void enableMockitoOnDevicesAndEmulators() {
if (System.getProperty(DEXMAKER_CACHE_PROPERTY) == null | System.getProperty(DEXMAKER_CACHE_PROPERTY).isEmpty()) {
file = new file(CACHE_DIRECTORY) ;
if (!file.exists()) {
final boolean success = file.mkdirs();
if (!success) {
failure(Impossible to create the required cache directory for Mockito);
}
}

System.setProperty(DEXMAKER_CACHE_PROPERTY, file.getPath());}}

The MainActivityFragment remains the same as above. So the test set looks like this:

com.klogi.myapplication;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.test.ActivityInstrumentationTestCase2 ;

import junit.framework.assert;

import static org.assertj.core.api.assertions.assertDat;
import static org.mockito.mockito;
import static org.mockito.Mockito.when ;

public class MainActivityFragmentTest extends ActivityInstrumentationTestCase2 {

public MainActivityFragmentTest() {
super(MainActivity.class);
}

Main activity Main activity;
Main activity Main activity Main activity

@Override
protected void setUp() throws an exception {
TestUtils.enableMockitoOnDevicesAndEmulators();
mainActivity = getActivity();
mainActivityFragment = new MainActivityFragment();
}

public void testMainActivity() {
Assert.assertNotNull(mainActivity);
}

public invalid testCowsCounter() {
startFragment(mainActivityFragment);
assertDat(mainActivityFragment.calculateYoungCows(10)).isEqualTo(2);
assertDat(mainActivityFragment.calculateYoungCows(99)).isEqualTo(3);
}

private void startFragment( fragment ) {
FragmentManager fragmentManager = mainActivity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(fragment, null);
fragmentTransaction.commit() ;

getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
getActivity().getSupportFragmentManager().executePendingTransactions();
}
})

getInstrumentation().waitForIdleSync();
}

}

As you can see, the Test class is an extension of the ActivityInstrumentationTestCase2 class.
It is also very important to pay attention to the method of splitting the startup, which has changed from the JUnit example: By default, the tests are not executed on the UI thread and we have to explicitly call the FragmentManager transactions waiting for execution.

– If you have the dagger

This is where it gets serious. .

First, we remove the ActivityInstrumentationTestCase2 class in favor of the ActivityUnitTestCase class as the base class for all test extract classes.

As always, it’s not that simple and there are some pitfalls (this is an example). So we need to move from our AcitivityUnitTestCase to the ActivityUnitTestCaseOverride.

It’s too long to publish here, so I downloaded the full version on github;

The public summary class ActivityUnitTestCaseOverride
extends ActivityUnitTestCase {

……..
private class mActivityClass ;

Private mActivityContext;
Private mApplication;
Private MockParent ;

private boolean mAttached = false;
private boolean mCreated = false ;

public ActivityUnitTestCaseOverride(Class activityClass) {
super(activityClass);
mActivityClass = activityClass;
}

@Override
public T getActivity() {
return (T) super.getActivity();
}

Override
protected void setUp() throws an exception {
super.setUp() ;

// default value for the target context, default
mActivityContext = getInstrumentation().getTargetContext();
}

/**
* Start the tested activity as if it were
* {@link android.content.Context#startActivity Context.startActivity()}, with the
* arguments it provided. If you use this method to start, it will automatically stop
* {@link #tearDown}.
*

 

*

This method callsCreate(), but if you want to delve into the
* methods of the cyclic life of the activity, you’ll have to call them yourself from your test case.
*

 

*

Do not call from the setUp() method. You should call this method from each of your test methods
*.
*
* @param Intent as associated with {@link android.content.Context#startActivity}.
* @param StoredInstanceState Instance state when you model this part of the loop
*. Normally lame.
* @param lastNonConfigurationInstance This object is available to Activity
* when it calls {@link android.app.Activity#getLastNonConfigurationInstance()}.
* Normally zero.
* @ Return Created
activity */
protected T startActivity(Intent, Bundle savedInstanceState,
Object lastNonConfigurationInstance) {
assertFalse(Activity already created, mCreated) ;

if (!mAttached) {
assertNotNull(mActivityClass);
setActivity(null);
T newActivity = null;
try {
IBinder token = null;
if (mApplication == null) {
setApplication(new MockApplication());
}
ComponentName cn = new ComponentName(getInstrumentation()).getTargetContext(), mActivityClass.getName());
intent.setComponent(cn);
ActivityInfo info = new ActivityInfo();
CharSequence title = mActivityClass.getName();
mMockParent = new MockParent();
String id = null ;

newActivity = (T) getInstrumentation().newActivityClass, mActivityContext,
token, mApplication, intention, info, title, mMockParent, id,
lastNonConfigurationInstance);
} catch (Exception e) {
assertNotNull(newActivity);
}.

assertNotNull(newActivity) ;
setActivity(newActivity) ;

Attached = true;
}

T result = getActivity();
if (result != null) {
getInstrumentation().callActivityOnCreate(getActivity(), savedInstanceState);
mCreated = true;
}
reverse result;
}

protected class getActivityClass() {
return mActivityClass;
}

@Override
protected void tearDown() throws Exception {

setActivity(null) ;

// Scrub out members – protects against memory leaks in case someone
// creates a non-static internal class (referring to a test case) and gives it
// to someone else to hold the
scrubClass (ActivityInstrumentationTestCase.class);

super.tearDown();
}

/**
* Install the application for use during testing. This function must be called before
* {@link #startActivity} is called. If your test does not call this method,
*
* @param application The object of the request to be entered in the activity to be tested.
*/
Common blank set application (Application) {
mApplication = Application;
}
…….
}

Create an AbstractFragmentTest for all your fragment tests:

import android.app.ActivityInfo; import android.content.intentional; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android support.v4.app.Fragment; import android support.v4.app.FragmentManager; import android support.v4.app.FragmentTransaction ;

/**
* Generic base class for {@link Fragment} tests.
*/
public abstract class AbstractFragmentTest extends ActivityUnitTestCaseOverride {

closed fragment of the TDF; MockInjectionRegistration
tamper-resistant ;

protected AbstractFragmentTest (fragment TFragment, Class activityType) {
super(activityType);
this.fragment = parameterIsNotNull(fragment);
}

@General
protected void setActivity(Activity testActivity) {
if (testActivity !=null) {
testActivity.setTheme(R.style.AppCompatTheme);
}

super.setActivity(testActivity);
}

/**
* Check {@link-fragment}
*/
protected TF-fragment getFragment() {
-fragment returned;
}

protected null setUpActivityAndFragment() {
createMockApplication() ;

Final intent = new intent (getInstrumentation().getTargetContext(),
getActivityClass());
startActivity(int, null, null);
startFragment(getFragment()) ;

getInstrumentation().callActivityOnStart(getActivity());
getInstrumentation().callActivityOnResume(getActivity());
}

private void createMockApplication() {
TestUtils.enableMockitoOnDevicesAndEmulators() ;

mocks = new MockInjectionRegistration();
testApplication testApplication = new TestApplication(getInstrumentation().getTargetContext());
testApplication.setModules(mocks);
testApplication.onCreate();
setApplication(testApplication);
}.

private void startFragment(Fragment) {
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(fragment, null);
fragmentTransaction.commit();
}
}

There are a few important things to consider.

1) We override the setActivity() method to activate the AppCompact theme. Without it, the test suit breaks.

2) the method setUpActivityAndFragment() :

I. creates an activity ( => getActivity() starts returning a non-zero value. in tests and in the tested application)
1) onCreate() the activity called ;

2) onStart() of the activity called ;

3) on Resume() of the activity called ;

II. Attach a fragment to the activity and start it up

1) onAttach() of the fragment called ;

2) onCreateView() of the fragment called ;

3) onStart() of the fragment called ;

4) on Resume() of the fragment called ;

3) Create theMoscAprication() method:
As with the daggerless version, we include a number of devices and emulators in Pre-Step 1.

We then replace the normal application with its injections with our custom application, TestApplication!

The recording of the fake injection looks like this:

….
Input javax.injection.Singleton ;

import dolk.module ;
import dolk.supplies ;
import de.greenrobot.event.eventBus ;

Import static org.mockito.mockito;
Import static org.mockito.mockito.when ;

@module(
injects = {.

….
MainActivity.class,
MyWorkFragment.class,
HomeFragment.class,
ProfileFragment.class,
…..
},
addsTo = DelveMobileInjectionRegistration.class,
overrides = true
)
public end class MockInjectionRegistration {

…..
public dataSource dataSource;
public EventBus eventBus;
public MixpanelAPI mixpanel;
……

public MockInjectionRegistration() {
…..
dataSource = mock(DataSource.class);
eventBus = mock(EventBus.class);
mixpanel = mock(MixpanelAPI.class);
MixpanelAPI.People mixpanelPeople = mock(MixpanelAPI.People.class);
when(mixpanel.getPeople()).thenReturn(mixpanelPeople);
……
}
………..
@Provided
@Single
@Disabled
// Called by Dagger
DataSource provideDataSource() {
Guard.valueINotNull(dataSource);
Provides dataSource;
}

@Bids
@Single element
@Error warnings
// Dagger call
EventBus provideEventBus() {
Guard.valueINotNull(eventBus);
Return eventBus;
}

@item
@item
// called by Dagger
MixpanelAPI provideMixpanelAPI() {
Guard.valueINotNull(mixpanel);
provide mixpanel;
}
………
}

That is, instead of real classes, we provide fragments with their fictional versions. (This is easy to follow, allows you to adjust the results of method calls, etc.).

And TestApplication is simply your own application extension that should support ObjectGraph’s configuration and initialization modules.

Those were the first steps to start writing the
tests. Now comes the easy part, the real tests:

The SearchFragmentTest public class extends AbstractFragmentTest {

public SearchFragmentTest() {
super(new SearchFragment(), MainActivity.class);
}

@UiThreadTest
public void testOnCreateView() throws an exception {
setUpActivityAndFragment() ;

SearchFragment searchFragment = getFragment();
assertNotNull(searchFragment.getSearchAdapter());
assertNotNull(searchFragment.getSearchSignalLogger());
assertNotNull(searchFragment.getSearchSignalLogger());
}

@UiThreadTest
public void testOnPause() throws an exception {
setUpActivityAndFragment() ;

SearchFragment searchFragment = getFragment();
assertTrue(Strings.isNullOrEmpty(SharedPreferencesTools.getString(getActivity(), SearchFragment.SEARCH_STATE_BUNDLE_ARGUMENT)))) ;

searchFragment.searchBoxRef.setCurrentConstraint(abs) ;
searchFragment.onPause() ;

assertEquals(searchFragment.searchBoxRef.getCurrentConstraint(), SharedPreferencesTools.getString(getActivity(), SearchFragment.SEARCH_STATE_BUNDLE_ARGUMENT))});}.

@UiThreadTest
public invalid testOnQueryTextChange() throws an exception {
setUpActivityAndFragment();
reset(mocks.eventBus) ;

getFragment().onQueryTextChange(Donald) ;
Thread.sleep(300) ;

// There must be one cached and one uncached event
verify(mocks.eventBus, times(2)).post(isA(SearchRequest.class));
verify(mocks.eventBus).post(isA(SearchLoadingIndicatorEvent.class));
}

@UiThreadTest
public invalid testOnQueryUpdateEventWithDifferentConstraint() throws an exception {
setUpActivityAndFragment() ;

reset(mocks.eventBus) ;

getFragment().onEventMainThread(new SearchResponse(new ArrayList(), Donald, false)) ;

checkNoMoreInteractions(mocks.eventBus);
}
….
}

That’s it!
You have now activated the Instrumental/Binding Tools tests for fragments.

I sincerely hope this post helps someone.

Solution 2:

Suppose you have a FragmentActivity class called MyFragmentActivity, where you add a public fragment class called MyFragmentTransaction. Just create a JUnit Test Case class that extends ActivityInstrumentationTestCase2 to your test project. Then just call getActivity() and get access to the MyFragment object and its public members to write test scripts.

See code below:

// TARGET CLASS
public class MyFragmentActivity extends FragmentActivity {
public MyFragment ;

Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState) ;
setContentView(R.layout.main) ;

FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
myFragment = new MyFragment();
fragmentTransaction.add(R.id.mainFragmentContainer, myFragment);
fragmentTransaction.commit();
}
}

// TEST CLASS
public class MyFragmentActivityTest extends android.test.activityInstrumentationTestCase2 {
MyFragmentActivity myFragmentActivity ;
MyFragment ;

public MyFragmentActivityTest() {
super(MyFragmentActivity.class);
}

@Override
protected void setUp() throws Exception {
super.setUp();
myFragmentActivity = (myFragmentActivity) getActivity();
myFragment = myFragmentActivity.myFragment;
}

public invalid testPreConditions() {
assertNotNull(myFragmentActivity);
assertNotNull(myFragment);
}

public invalid testAnythingFromMyFragment() {
// Access all public members of myFragment for test
}
}

I hope this helps. Please accept my answer if you find it helpful. Thank you!

Solution 3:

I’m sure you could do as you say, create a simulated activity and test a clip from there.
Simply export the compatibility library to the main project and you can access the fragments from the test project.
I will create a sample project here, test the code and update my answer based on my findings.

For more information on exporting the Compatibility Library, click here.

Solution 4:

Added to @abhijit.mitkar’s reply.

Provided that your fragment is not a public participant in the activity being tested.

protected null setUp() {
mActivity = getActivity() ;
mFragment = new TheTargetFragment() ;

FragmentTransaction transaction = mActivity.getSupportFragmentManager().beginTransaction();
transaction.add(R.id.fragment_container, mFragment, FRAGMENT_TAG);
transaction.commit();
}

The purpose of the above code is to replace the fragment with a new fragment object that we have access to.

Use the following code to access the members of the Snippet user interface.

TextView randomTextView= (TextView) mFragment.getView().findViewById(R.id.textViewRandom) ;

When you extract a user interface from an activity, the expected result is not obtained.

TextView randomTextView= (TextView) mActivity.findViewById(R.id.textViewRandom) ;

Finally, if you want to make some changes to the user interface. Just like a good Android developer in the mainstream.

mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
//set text view value
}
})

Pay attention:
You can give it Thread.sleep() every time the test ends. To avoid blocking, getInstrumentation().waitForIdleSync(); doesn’t always seem to work.

I have been using the ActivityInstrumentationTestCase2 since I started running functional tests.

Good luck!

The item How to Configure Unit Testing for Fragmentation in Android – Sharing and Storing Technology Knowledge appeared first on.

 espresso check if fragment is displayedrobolectric test fragmentfragmentscenario javafragmentscenario emptyfragmentactivityandroid-espresso get current fragmentfragmentfactory exampletestnavhostcontrollernavigation testing in software testingandroid activity scenariotest cases for navigation barnavigation testing is type of which testing.android-espresso bottomnavigationview clicklaunchfragmentincontainer not foundjava launchfragmentincontainerfragment test is performed onandroid sharedtestrobolectric androidx fragmentrunwith(androidjunit4 class)robolectric tutorialactivitytestrule exampleespresso check if activity launchedrobolectric enter textrobolectric fragment test exampleespresso testing fragmentsjunit vs espressoandroid-espresso fragment recyclerviewintentstestruleandroid espresso toastespresso perform(click)android unit test example githubunit testing android studiounit test for login page androidandroid unit testing mediuminstrumentationregistry not foundactivitytestruleandroid unit test fragment examplefragment-testing androidfragmentscenario dialogfragmentandroid fragment-testing javadialogfragment unit test

You May Also Like