Unidad probar un fragmento de Android

Quiero probar una unidad en una clase Fragmento Android.

¿Puedo configurar una prueba con AndroidTestCase o necesito usar ApplicationTestCase?

¿Hay ejemplos útiles de cómo se pueden usar estos dos TestCases? Los ejemplos de pruebas en el sitio del desarrollador son mínimos y parecen centrarse en las actividades de prueba.

Todo lo que he encontrado en otros lugares son ejemplos donde la clase AndroidTestCase se extiende pero luego todo lo que está probado es agregar dos números juntos o si el Contexto es usado, simplemente hace un simple obtener y prueba que algo no es nulo!

Según lo entiendo, un Fragmento tiene que vivir dentro de una Actividad. ¿Podría crear una Actividad simulada o obtener la Aplicación o el Contexto para proporcionar una Actividad dentro de la cual puedo probar mi Fragmento?

¿Necesito crear mi propia actividad y luego usar ActivityUnitTestCase?

Gracias por tu ayuda.

Trev

Yo estaba luchando con la misma pregunta. Especialmente, como la mayoría de los ejemplos de código ya están obsoletos + Android Studio / SDKs está mejorando, por lo que las viejas respuestas a veces no son relevantes.

Por lo tanto, las primeras cosas primero: usted necesita para determinar si desea utilizar Instrumental o simples pruebas JUnit .

La diferencia entre ellos descrita maravillosamente por SD aquí ; En resumen: las pruebas de JUnit son más ligeras y no requieren un emulador para ejecutar, Instrumental – le dan lo más cercano a la experiencia del dispositivo real posible (sensores, gps, la interacción con otras aplicaciones, etc). También lee más acerca de las pruebas en Android .

1. Prueba JUnit de fragmentos

Digamos que usted no necesita pruebas instrumentales pesadas y simples pruebas junit son suficientes. Utilizo el marco agradable Robolectric para este propósito.

En gradle añadir:

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-annotations' } ..... } 

Mockito, AsserJ son opcionales, pero los encontré muy útiles así que recomiendo encarecidamente incluirlos también.

A continuación, en Variantes de construcción especifique Pruebas de unidad como un artefacto de prueba : Introduzca aquí la descripción de la imagen

Ahora es el momento de escribir algunas pruebas reales 🙂 Como ejemplo, vamos a tomar el proyecto de ejemplo "Blank Activity with Fragment" estándar.

He añadido algunas líneas de código, para tener realmente algo que probar:

 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.ArrayList; import java.util.List; public class MainActivityFragment extends Fragment { private List<Cow> cows; public MainActivityFragment() {} @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { cows = new ArrayList<>(); cows.add(new Cow("Burka", 10)); cows.add(new Cow("Zorka", 9)); cows.add(new Cow("Kruzenshtern", 15)); return inflater.inflate(R.layout.fragment_main, container, false); } int calculateYoungCows(int maxAge) { if (cows == null) { throw new IllegalStateException("onCreateView hasn't been called"); } if (getActivity() == null) { throw new IllegalStateException("Activity is null"); } if (getView() == null) { throw new IllegalStateException("View is null"); } int result = 0; for (Cow cow : cows) { if (cow.age <= maxAge) { result++; } } return result; } } 

Y clase vaca:

 public class Cow { public String name; public int age; public Cow(String name, int age) { this.name = name; this.age = age; } } 

El conjunto de pruebas de Robolectic sería algo como:

 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.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.annotation.Config; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, sdk=21) public class MainActivityFragmentTest extends ApplicationTestCase<Application> { public MainActivityFragmentTest() { super(Application.class); } MainActivity mainActivity; MainActivityFragment mainActivityFragment; @Before public void setUp() { mainActivity = Robolectric.setupActivity(MainActivity.class); mainActivityFragment = new MainActivityFragment(); startFragment(mainActivityFragment); } @Test public void testMainActivity() { Assert.assertNotNull(mainActivity); } @Test public void testCowsCounter() { assertThat(mainActivityFragment.calculateYoungCows(10)).isEqualTo(2); assertThat(mainActivityFragment.calculateYoungCows(99)).isEqualTo(3); } private void startFragment( Fragment fragment ) { FragmentManager fragmentManager = mainActivity.getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(fragment, null ); fragmentTransaction.commit(); } } 

Es decir, creamos actividad a través de Robolectric.setupActivity , nuevo fragmento en el setUp () de las clases de prueba. Opcionalmente, puede iniciar inmediatamente el fragmento desde setUp () o puede hacerlo directamente desde la prueba.

¡NÓTESE BIEN! No he pasado demasiado tiempo en él, pero parece que es casi imposible unirlo con Dagger (no sé si es más fácil con Dagger2), ya que no se puede configurar la aplicación de prueba personalizada con inyecciones burlones.

2. Ensayo instrumental de fragmentos

La complejidad de este enfoque depende en gran medida de si está utilizando Dagger / Dependency injection en la aplicación que desea probar.

En Variantes de compilación, especifique pruebas instrumentales de Android como un artefacto de prueba : Introduzca aquí la descripción de la imagen

En Gradle agrego estas dependencias:

 dependencies { ..... androidTestCompile "com.google.dexmaker: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" } ..... } 

(De nuevo, casi todos ellos son opcionales, pero pueden hacer su vida mucho más fácil)

– Si no tienes Dagger

Este es un camino feliz. La diferencia con Robolectric de lo anterior sería sólo en pequeños detalles.

Pre-paso 1 : Si vas a usar Mockito, debes habilitarlo para que se ejecute en los dispositivos y emuladores con este hack:

 public class TestUtils { 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 file = new File(CACHE_DIRECTORY); if (!file.exists()) { final boolean success = file.mkdirs(); if (!success) { fail("Unable to create cache directory required for Mockito"); } } System.setProperty(DEXMAKER_CACHE_PROPERTY, file.getPath()); } } } 

El MainActivityFragment permanece igual que el anterior. Así que el conjunto de prueba se vería así:

 package 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.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class MainActivityFragmentTest extends ActivityInstrumentationTestCase2<MainActivity> { public MainActivityFragmentTest() { super(MainActivity.class); } MainActivity mainActivity; MainActivityFragment mainActivityFragment; @Override protected void setUp() throws Exception { TestUtils.enableMockitoOnDevicesAndEmulators(); mainActivity = getActivity(); mainActivityFragment = new MainActivityFragment(); } public void testMainActivity() { Assert.assertNotNull(mainActivity); } public void testCowsCounter() { startFragment(mainActivityFragment); assertThat(mainActivityFragment.calculateYoungCows(10)).isEqualTo(2); assertThat(mainActivityFragment.calculateYoungCows(99)).isEqualTo(3); } private void startFragment( Fragment 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(); } } 

Como se puede ver, la clase Test es una extensión de la clase ActivityInstrumentationTestCase2 . Además, es muy importante prestar atención al método startFragment , que ha cambiado comparándolo con el ejemplo de JUnit: de forma predeterminada, las pruebas no se ejecutan en el subproceso de la interfaz de usuario y necesitamos solicitar explícitamente la ejecución pendiente de las transacciones de FragmentManager.

– Si tienes Daga

Las cosas se están poniendo serias aquí 🙂

En primer lugar, nos estamos deshaciendo de ActivityInstrumentationTestCase2 en favor de la clase ActivityUnitTestCase , como una clase base para las clases de prueba de todos los fragmentos.

Como de costumbre, no es tan simple y hay varias trampas ( este es uno de los ejemplos). Así que tenemos que proxeneta nuestra AcitivityUnitTestCase a ActivityUnitTestCaseOverride

Es un poco demasiado tiempo para publicarlo completamente aquí, por lo que subir la versión completa de la misma a github ;

 public abstract class ActivityUnitTestCaseOverride<T extends Activity> extends ActivityUnitTestCase<T> { ........ private Class<T> mActivityClass; private Context mActivityContext; private Application mApplication; private MockParent mMockParent; private boolean mAttached = false; private boolean mCreated = false; public ActivityUnitTestCaseOverride(Class<T> activityClass) { super(activityClass); mActivityClass = activityClass; } @Override public T getActivity() { return (T) super.getActivity(); } @Override protected void setUp() throws Exception { super.setUp(); // default value for target context, as a default mActivityContext = getInstrumentation().getTargetContext(); } /** * Start the activity under test, in the same way as if it was started by * {@link android.content.Context#startActivity Context.startActivity()}, providing the * arguments it supplied. When you use this method to start the activity, it will automatically * be stopped by {@link #tearDown}. * <p/> * <p>This method will call onCreate(), but if you wish to further exercise Activity life * cycle methods, you must call them yourself from your test case. * <p/> * <p><i>Do not call from your setUp() method. You must call this method from each of your * test methods.</i> * * @param intent The Intent as if supplied to {@link android.content.Context#startActivity}. * @param savedInstanceState The instance state, if you are simulating this part of the life * cycle. Typically null. * @param lastNonConfigurationInstance This Object will be available to the * Activity if it calls {@link android.app.Activity#getLastNonConfigurationInstance()}. * Typically null. * @return Returns the Activity that was created */ protected T startActivity(Intent 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().newActivity(mActivityClass, mActivityContext, token, mApplication, intent, info, title, mMockParent, id, lastNonConfigurationInstance); } catch (Exception e) { assertNotNull(newActivity); } assertNotNull(newActivity); setActivity(newActivity); mAttached = true; } T result = getActivity(); if (result != null) { getInstrumentation().callActivityOnCreate(getActivity(), savedInstanceState); mCreated = true; } return result; } protected Class<T> getActivityClass() { return mActivityClass; } @Override protected void tearDown() throws Exception { setActivity(null); // Scrub out members - protects against memory leaks in the case where someone // creates a non-static inner class (thus referencing the test case) and gives it to // someone else to hold onto scrubClass(ActivityInstrumentationTestCase.class); super.tearDown(); } /** * Set the application for use during the test. You must call this function before calling * {@link #startActivity}. If your test does not call this method, * * @param application The Application object that will be injected into the Activity under test. */ public void setApplication(Application application) { mApplication = application; } ....... } 

Cree un AbstractFragmentTest abstracto para todas sus pruebas de fragmentos:

 import android.app.Activity; import android.content.Intent; 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; /** * Common base class for {@link Fragment} tests. */ public abstract class AbstractFragmentTest<TFragment extends Fragment, TActivity extends FragmentActivity> extends ActivityUnitTestCaseOverride<TActivity> { private TFragment fragment; protected MockInjectionRegistration mocks; protected AbstractFragmentTest(TFragment fragment, Class<TActivity> activityType) { super(activityType); this.fragment = parameterIsNotNull(fragment); } @Override protected void setActivity(Activity testActivity) { if (testActivity != null) { testActivity.setTheme(R.style.AppCompatTheme); } super.setActivity(testActivity); } /** * Get the {@link Fragment} under test. */ protected TFragment getFragment() { return fragment; } protected void setUpActivityAndFragment() { createMockApplication(); final Intent intent = new Intent(getInstrumentation().getTargetContext(), getActivityClass()); startActivity(intent, 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 fragment) { FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(fragment, null); fragmentTransaction.commit(); } } 

Hay varias cosas importantes aquí.

1) Anulamos el método setActivity () para establecer el tema de AppCompact en la actividad. Sin eso, traje de prueba se bloqueará.

2 ) método setUpActivityAndFragment ():

I. crea la actividad (=> getActivity () comienza a devolver el valor no nulo, en las pruebas y en la aplicación que está bajo prueba) 1) onCreate () de la actividad llamada;

2) onStart () de la actividad llamada;

3) onResume () de la actividad llamada;

II. Adjuntar y iniciar fragmento a la actividad

1) onAttach () del fragmento llamado;

2) onCreateView () del fragmento llamado;

3) onStart () del fragmento llamado;

4) onResume () del fragmento llamado;

3) Método createMockApplication (): Al igual que en la versión no dagger, en Pre-step 1, habilitamos el mooting en los dispositivos y en los emuladores.

A continuación, reemplazar la aplicación normal con sus inyecciones con nuestra costumbre, TestApplication!

MockInjectionRegistration se parece a:

 .... import javax.inject.Singleton; import dagger.Module; import dagger.Provides; import de.greenrobot.event.EventBus; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @Module( injects = { .... MainActivity.class, MyWorkFragment.class, HomeFragment.class, ProfileFragment.class, .... }, addsTo = DelveMobileInjectionRegistration.class, overrides = true ) public final 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); ..... } ........... @Provides @Singleton @SuppressWarnings("unused") // invoked by Dagger DataSource provideDataSource() { Guard.valueIsNotNull(dataSource); return dataSource; } @Provides @Singleton @SuppressWarnings("unused") // invoked by Dagger EventBus provideEventBus() { Guard.valueIsNotNull(eventBus); return eventBus; } @Provides @Singleton @SuppressWarnings("unused") // invoked by Dagger MixpanelAPI provideMixpanelAPI() { Guard.valueIsNotNull(mixpanel); return mixpanel; } ......... } 

Es decir, en lugar de clases reales, estamos proporcionando a los fragmentos sus versiones burladas. (Que son fácilmente rastreables, permite configurar los resultados de las llamadas de método, etc).

Y el TestApplication es sólo su extensión personalizada de la aplicación, que debe apoyar los módulos de configuración e inicializar el ObjectGraph.

Estos fueron pre-pasos para empezar a escribir las pruebas 🙂 Ahora la parte simple, las pruebas reales:

 public class SearchFragmentTest extends AbstractFragmentTest<SearchFragment, MainActivity> { public SearchFragmentTest() { super(new SearchFragment(), MainActivity.class); } @UiThreadTest public void testOnCreateView() throws Exception { setUpActivityAndFragment(); SearchFragment searchFragment = getFragment(); assertNotNull(searchFragment.adapter); assertNotNull(SearchFragment.getSearchAdapter()); assertNotNull(SearchFragment.getSearchSignalLogger()); } @UiThreadTest public void testOnPause() throws 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 void testOnQueryTextChange() throws Exception { setUpActivityAndFragment(); reset(mocks.eventBus); getFragment().onQueryTextChange("Donald"); Thread.sleep(300); // Should be one cached, one uncached event verify(mocks.eventBus, times(2)).post(isA(SearchRequest.class)); verify(mocks.eventBus).post(isA(SearchLoadingIndicatorEvent.class)); } @UiThreadTest public void testOnQueryUpdateEventWithDifferentConstraint() throws Exception { setUpActivityAndFragment(); reset(mocks.eventBus); getFragment().onEventMainThread(new SearchResponse(new ArrayList<>(), "Donald", false)); verifyNoMoreInteractions(mocks.eventBus); } .... } 

¡Eso es! Ahora tienes habilitado las pruebas de Instrumental / JUnit para tus Fragmentos.

Espero sinceramente que este post ayude a alguien.

Suponga que tiene una clase FragmentActivity llamada 'MyFragmentActivity' en la que se agrega una clase Fragment pública llamada 'MyFragment' mediante FragmentTransaction. Basta con crear una clase de 'JUnit Test Case' que extienda ActivityInstrumentationTestCase2 en su proyecto de prueba. A continuación, simplemente llame a getActivity () y acceda al objeto MyFragment ya sus miembros públicos para escribir casos de prueba.

Consulte el siguiente fragmento de código:

 // TARGET CLASS public class MyFragmentActivity extends FragmentActivity { public MyFragment 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 myFragmentActivity; MyFragment myFragment; public MyFragmentActivityTest() { super(MyFragmentActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); myFragmentActivity = (MyFragmentActivity) getActivity(); myFragment = myFragmentActivity.myFragment; } public void testPreConditions() { assertNotNull(myFragmentActivity); assertNotNull(myFragment); } public void testAnythingFromMyFragment() { // access any public members of myFragment to test } } 

Espero que esto ayude. Acepte mi respuesta si encuentra esto útil. Gracias.

Estoy bastante seguro de que puedes hacer lo que estás diciendo, crear una Actividad simulada y probar el fragmento desde allí. Sólo tienes que exportar la biblioteca de compatibilidad en el proyecto principal y podrás acceder a los fragmentos del proyecto de prueba. Voy a crear un proyecto de ejemplo y probar el código aquí y actualizará mi respuesta en base a lo que descubro.

Para obtener más detalles sobre cómo exportar la biblioteca de compatibilidad, compruebe aquí .

Añadiendo a la respuesta de @ abhijit.mitkar.

Dado un escenario que su fragmento no es un miembro público en la actividad bajo prueba.

 protected void setUp() { mActivity = getActivity(); mFragment = new TheTargetFragment(); FragmentTransaction transaction = mActivity.getSupportFragmentManager().beginTransaction(); transaction.add(R.id.fragment_container, mFragment, "FRAGMENT_TAG"); transaction.commit(); } 

El propósito del código anterior es reemplazar el fragmento con un nuevo objeto de fragmento al que tenemos acceso.

El código siguiente le permitirá tener acceso a los fragmentos de los miembros de la interfaz de usuario.

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

Obtener la interfaz de usuario de la actividad no le dará el resultado esperado.

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

Finalmente si desea hacer algunos cambios en la interfaz de usuario. Al igual que un buen desarrollador de Android hacerlo en el hilo principal.

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

Nota: Es posible que desee darle un Thread.sleep () cada una termina una prueba. Para evitar el bloqueo, getInstrumentation (). WaitForIdleSync (); No parece funcionar siempre.

He utilizado ActivityInstrumentationTestCase2 desde que estaba haciendo pruebas funcionales.

  • Android: consigue el i-ésimo TextView dentro de un ListView
  • ¿Cómo probar la clase usando resolver contenido / proveedor?
  • Android instrumentación prueba java.lang.UnsatisfiedLinkError en el uso de AndroidJunitRunner y AndroidJUnit4
  • ¿Cómo puedo ejecutar una única prueba de Android con Kotlin?
  • ¿Puedo probar las notificaciones de la barra de estado utilizando el marco de pruebas de Android?
  • Cómo obtener la salida de registro de Android mostrada con las pruebas de JUnit (utilizando JUnit nativo sin emulador)
  • Robolectric InflateException al usar el diseño de barra de acción personalizada
  • Error en el corredor al realizar pruebas con Robolectric
  • Prueba de unidad de Android no se burla
  • Cómo escribir una prueba de unidad para un controlador de excepción de subprocesos no detectados.
  • Cómo proporcionar archivos de datos para las pruebas de unidad de Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.