¿Cómo se prueba una aplicación de Android en múltiples actividades?

80

Estamos creando una aplicación de Android compleja que consta de muchas pantallas y flujos de trabajo distribuidos en muchas Actividades. Nuestros flujos de trabajo son similares a los que puede ver en el cajero automático de un banco, por ejemplo, hay un Activityinicio de sesión que cambia a un menú principal Activityque puede pasar a otras actividades según las opciones del usuario.

Dado que tenemos tantos flujos de trabajo, necesitamos crear pruebas automatizadas que abarquen múltiples actividades para que podamos probar un flujo de trabajo de un extremo a otro. Por ejemplo, usando el ejemplo del cajero automático, querríamos ingresar un PIN válido, verificar que nos envía al menú principal, elegir retirar efectivo, verificar que estamos en la pantalla de retiro de efectivo, etc., etc., y finalmente encontrarnos de nuevo en el menú principal o "desconectado".

Hemos jugado con las API de prueba que vienen con Android (por ejemplo ActivityInstrumentationTestCase2) y también con Positron , pero ninguna parece capaz de probar más allá de los límites de una sola Activity, y aunque podemos encontrar alguna utilidad en estas herramientas para algunas pruebas unitarias, ganaron. Satisface nuestras necesidades de escenarios de prueba que abarcan múltiples Actividades.

Estamos abiertos a un marco xUnit, secuencias de comandos, grabadoras / reproducciones de GUI, etc. y agradeceríamos cualquier consejo.

Un solo tiro
fuente
2
A partir de Android 4.1, ahora hay un nuevo marco de prueba de Android que permite realizar pruebas en todas las actividades y, de hecho, en todo el sistema: developer.android.com/tools/testing/testing_ui.html
Christopher Orr
1
Robotium también se adaptará a esta necesidad y en solo un par de líneas.
Dori

Respuestas:

65

Me siento un poco incómodo por responder mi propia pregunta sobre recompensas, pero aquí está ...

He buscado alto y bajo sobre esto y no puedo creer que no haya una respuesta publicada en ninguna parte. Me he acercado mucho. Definitivamente puedo ejecutar pruebas que abarcan actividades ahora, pero mi implementación parece tener algunos problemas de sincronización donde las pruebas no siempre pasan de manera confiable. Este es el único ejemplo que conozco de pruebas exitosas en múltiples actividades. Con suerte, mi extracción y anonimización no introdujo errores. Esta es una prueba simplista en la que escribo un nombre de usuario y contraseña en una actividad de inicio de sesión, y luego observo que se muestra un mensaje de bienvenida adecuado en una actividad de "bienvenida" diferente:

package com.mycompany;

import android.app.*;
import android.content.*;
import android.test.*;
import android.test.suitebuilder.annotation.*;
import android.util.*;
import android.view.*;
import android.widget.*;

import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
import static com.mycompany.R.id.*;

public class LoginTests extends InstrumentationTestCase {

   @MediumTest
   public void testAValidUserCanLogIn() {

      Instrumentation instrumentation = getInstrumentation();

      // Register we are interested in the authentication activiry...
      Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(AuthenticateActivity.class.getName(), null, false);

      // Start the authentication activity as the first activity...
      Intent intent = new Intent(Intent.ACTION_MAIN);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      intent.setClassName(instrumentation.getTargetContext(), AuthenticateActivity.class.getName());
      instrumentation.startActivitySync(intent);

      // Wait for it to start...
      Activity currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Type into the username field...
      View currentView = currentActivity.findViewById(username_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyUsername");

      // Type into the password field...
      currentView = currentActivity.findViewById(password_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyPassword");

      // Register we are interested in the welcome activity...
      // this has to be done before we do something that will send us to that
      // activity...
      instrumentation.removeMonitor(monitor);
      monitor = instrumentation.addMonitor(WelcomeActivity.class.getName(), null, false);

      // Click the login button...
      currentView = currentActivity.findViewById(login_button;
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(Button.class));
      TouchUtils.clickView(this, currentView);

      // Wait for the welcome page to start...
      currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Make sure we are logged in...
      currentView = currentActivity.findViewById(welcome_message);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(TextView.class));
      assertThat(((TextView)currentView).getText().toString(), is("Welcome, MyUsername!"));
   }
}

Obviamente, este código no es muy legible. De hecho, lo he extraído en una biblioteca simple con una API similar al inglés, así que puedo decir cosas como esta:

type("myUsername").intoThe(username_field);
click(login_button);

Probé a una profundidad de aproximadamente 4 actividades y estoy satisfecho de que el enfoque funcione, aunque, como dije, parece haber un problema de tiempo ocasional que no he descubierto por completo. Todavía estoy interesado en conocer otras formas de realizar pruebas en todas las actividades.

Un solo tiro
fuente
3
Puede intentar agregar la anotación FlakyTest para repetir la prueba automáticamente cuando los problemas de tiempo hacen que falle. En realidad, no es una solución, pero sí una solución viable en algunas situaciones.
Carl Manaster
¡Gracias por escribir esto! Estaba buscando algo con la funcionalidad de ActivityMonitors para mis pruebas. Simplemente no pude encontrarlos.
Peter Ajtai
Hasta donde yo sé, nada de lo que hizo anteriormente no se puede hacer usandoActivityInstrumentationTestCase2
ericn
cualquier idea, en qué condición, 'getInstrumentation (). waitForIdleSync ();' entraría en un bucle infinito? En la placa del procesador en ejecución de Android 4.4.2_r2, me enfrento a este problema mientras ejecuto la prueba CTS.
ArunJTS
Creo que mi hijo @ pajato1 encontró y solucionó su problema de sincronización. Su solución resolvió mi problema. Esto es lo que dijo: "Acabo de notar en el javadoc que Instrumentation.startActivitySync () estaba bloqueando hasta que la nueva Actividad estaba lista y luego la devolvía, por lo que parece que el Monitor no era necesario. Quitarlo demostró que esto era correcto. Mi La teoría es que Monitor estaba provocando que la Actividad creada por startActivitySync () se reiniciara en algunos casos debido a una condición de carrera. Pasé algún tiempo leyendo el código fuente de Android, pero nada me llamó la atención como la causa de la condición de carrera. "
pajato0
22

Eche un vistazo a Robotium, 'un marco de prueba de código abierto creado para hacer que las pruebas automáticas de caja negra de las aplicaciones de Android sean significativamente más rápidas y fáciles de lo que es posible con las pruebas de instrumentación de Android listas para usar
'.

Inicio: http://www.robotium.org/
Fuente: http://github.com/jayway/robotium

Tenga en cuenta que el proyecto Robotium es mantenido por la empresa para la que trabajo

Jonas Söderström
fuente
hola, ¿hay una herramienta de grabación para esto? Revisé muchos sitios web y encontré testdroid que registra los scripts y lo ejecuta. Desafortunadamente, no es un programa gratuito, ¿conoce algún programa gratuito que realice el proceso de grabación?
thndrkiss
@thndrkiss: No conozco ninguna herramienta de este tipo. Si coloca una pregunta en el foro de Robotium, es probable que obtenga una mejor respuesta.
Jonas Söderström
2
Robotium es un salvavidas. Hará que tu prueba sea extremadamente fácil de escribir (básicamente estás hablando con él en inglés simple: haz clic en esto, presiona el botón Atrás, etc.) Puedes probar cualquier cosa, pero no es necesario que conozcas los pequeños detalles. Tiene al menos dos beneficios principales: puede probar aplicaciones de las que no tiene el código fuente, y se basa en la interfaz de usuario que lo hace muy robusto (cambia sus controladores / modelos mucho más que sus vistas ...)
tiktak
8

Siempre puedes usar Robotium. Admite pruebas de caja negra como Selenium pero para Android. Lo encontrará en Robotium.org

Renas
fuente
1
La última vez que verifiqué Robotium no se pudo usar en todas las actividades. ¿Eso ha sido arreglado ahora? stackoverflow.com/questions/3840034/…
user77115
3
Siempre ha funcionado en todas las actividades siempre que pertenezcan al mismo proceso.
Renas
4

Me sorprende que nadie haya mencionado algunas de las principales herramientas de pruebas funcionales automatizadas . En comparación con Robotium, estos no requieren escribir código Java.

MonkeyTalk : una herramienta de código abierto respaldada por la empresa Gorilla Logic. Ventajas: proporciona grabación y un lenguaje de scripting de alto nivel más fácil para los usuarios no técnicos y es multiplataforma (incluye iOS). Dados esos beneficios como requisitos, hemos descubierto que esta es la mejor solución. También permite la personalización más allá de lo que se puede hacer en su lenguaje de scripting usando Javascript.

Calabash-Android : una herramienta de código abierto para funciones de estilo pepino. Ventajas: escriba características en el lenguaje Gherkin, que es legible para empresas, lenguaje específico de dominio que le permite describir el comportamiento del software sin detallar cómo se implementa ese comportamiento. Hay disponible soporte similar pero no exacto para iOS en cucumber-ios . Las capacidades de grabación no son tan buenas, ya que producen una salida binaria.

Un par de otras referencias:

John Lehmann
fuente
3

Creé una herramienta de grabación y reproducción para Android y la puse a disposición en GitHub . Es fácil de configurar y usar, no requiere programación, se ejecuta en dispositivos reales (que no tienen que estar enraizados) y guarda automáticamente las capturas de pantalla mientras reproduce las pruebas.

Brian Kyckelhahn
fuente
Esto parece prometedor. Para aquellos que no ven el punto: esta parece ser una solución bastante buena para probar los gestos (tocar, arrastrar y otras cosas)
tiktak
3

En primer lugar, use 'ActivityInstrumentationTestCase2', no 'InstrumentationTestCase', como su clase base. Utilizo Robotium y pruebo rutinariamente en múltiples Actividades. Descubrí que tengo que especificar la actividad de inicio de sesión como el tipo genérico (y el argumento de clase para el constructor).

El constructor 'ActivityInstrumentationTestCase2' ignora el argumento del paquete y no lo requiere. El constructor que toma el paquete está en desuso.

De los Javadocs: "ActivityInstrumentationTestCase2 (String pkg, Class activityClass) Este constructor está en desuso. Utilice ActivityInstrumentationTestCase2 (Class) en su lugar"

El uso de la clase base recomendada permite que el marco maneje ciertos estándares, como comenzar su actividad. Eso se hace mediante la llamada a 'getActivity ()', si es necesario.

Lew Bloch
fuente
3

Encontré esto útil con un par de modificaciones. En primer lugar getInstrumentation().waitForIdleSync(), curará la descamación de la que habla SingleShot y también InstrumentationTestCasetiene una lauchActivityfunción que puede reemplazar las líneas de actividad de inicio.

mecanografiar
fuente
2

puede hacerlo así para evitar que los tiempos de espera de las escamas no estén sincronizados:

final Button btnLogin = (Button) getActivity().findViewById(R.id.button);
Instrumentation instrumentation = getInstrumentation();

// Register we are interested in the authentication activity...
Instrumentation.ActivityMonitor aMonitor = 
        instrumentation.addMonitor(mynextActivity.class.getName(), null, false);

getInstrumentation().runOnMainSync(new Runnable() {
         public void run() {
             btnLogin.performClick();
         }
     });

getInstrumentation().waitForIdleSync();

//check if we got at least one hit on the new activity
assertTrue(getInstrumentation().checkMonitorHit(aMonitor, 1)); 
j2emanue
fuente
1

Estoy trabajando en prácticamente lo mismo, y probablemente iré con una variación de la respuesta aceptada a esta pregunta, pero encontré Calculuon ( gitHub ) durante mis búsquedas de una solución.

Peter Ajtai
fuente
0

No lo he usado personalmente, pero ApplicationTestCase parece que podría ser lo que estás buscando.

Eric
fuente
Desafortunadamente, no hay ejemplos que indiquen que ese sea el caso.
SingleShot
Sí, parece que tienes razón ... fue engañado por el nombre. No puedo entender este. El mejor enfoque que he tenido hasta ahora es usar ActivityUnitTestCase de positron para verificar que se inicie la siguiente actividad, pero eso no te ayuda a construir historias coherentes. Alternativamente, InstrumentationTestCase.launchActivity podría permitirle iniciar una cantidad arbitraria de actividades, pero todavía estoy tratando de averiguar el tema de la Instrumentación.
Eric
0

¿El enfoque aceptado funcionará con diferentes actividades de diferentes aplicaciones, firmadas por diferentes certificados? Si no es así, Robotium es la mejor manera de probar actividades dentro de la misma aplicación.

usuario643154
fuente
0

Hay otra forma de hacer la actividad múltiple usando la clase ActivityInstrumentation. Es un escenario de automatización normal ... Primero obtenga el foco de cualquier objeto que desee y luego envíe una clave Simple como ese código de muestra

button.requestFocus();
sendKeys(KeyEvent.KEYCODE_ENTER);

Lo único que nos ayudará es comprender todas las llamadas a la API.

sandeep
fuente
0

Esta respuesta se basa en la respuesta aceptada, pero modificada para resolver el problema del tiempo que para mí se volvió consistente después de agregar alrededor de media docena de pruebas. @ pajato1 recibe el crédito por resolver el problema del tiempo, como se cita en los comentarios de respuesta aceptados.

/**
 * Creates a test Activity for a given fully qualified test class name.
 *
 * @param fullyQualifiedClassName The fully qualified name of test activity class.
 *
 * @return The test activity object or null if it could not be located.
 */
protected AbstractTestActivity getTestActivity(final String fullyQualifiedClassName) {
    AbstractTestActivity result = null;

    // Register our interest in the given activity and start it.
    Log.d(TAG, String.format("Running test (%s) with main class: %s.", getName(), fullyQualifiedClassName));
    instrumentation = getInstrumentation();

    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setClassName(instrumentation.getTargetContext(), fullyQualifiedClassName);
    // Wait for the activity to finish starting
    Activity activity = instrumentation.startActivitySync(intent);

    // Perform basic sanity checks.
    assertTrue("The activity is null!  Aborting.", activity != null);
    String format = "The test activity is of the wrong type (%s).";
    assertTrue(String.format(format, activity.getClass().getName()), activity.getClass().getName().equals(fullyQualifiedClassName));
    result = (AbstractTestActivity) activity;

    return result;
}
pajato0
fuente
0

Prueba las pruebas de la herramienta Monkey

Paso 1:

abra la terminal de estudio de Android (Herramientas-> terminal abierta)

Paso 2:

Para usar monkey, abra un símbolo del sistema y simplemente navegue al siguiente directorio.

 export PATH=$PATH:/home/adt-bundle-linux-x86-20140702/sdk/platform-tools

Paso 3:

agregue este comando de mono en la terminal y presione enter ..

vea la magia en su emulador.

adb shell monkey -p com.example.yourpackage -v 500

500: es el recuento de frecuencia o el número de eventos que se enviarán para la prueba.

puedes cambiar este recuento ..

Más referencia,

http://www.tutorialspoint.com/android/android_testing.htm

http://androidtesting.blogspot.in/2012/04/android-testing-with-monkey-tool.html

Ranjith Kumar
fuente
Los votantes negativos deben decir el motivo de la votación negativa ... este es un código de trabajo ... y también un método de prueba oficial. Si hay algún error, estoy listo para corregir mi respuesta ..
Ranjith Kumar