Buscar..


Introducción

El enfoque de este documento es representar objetivos y formas de escribir la interfaz de usuario de Android y las pruebas de integración. Google proporciona el expreso y el UIAutomator, por lo que el enfoque debe estar en torno a estas herramientas y sus respectivos envoltorios, por ejemplo, Appium, Spoon, etc.

Sintaxis

  • Recurso inactivo
  • Cadena getName (): devuelve el nombre del recurso inactivo (utilizado para el registro y la idempotencia del registro).
  • boolean isIdleNow (): devuelve true si el recurso está actualmente inactivo.
  • void registerIdleTransitionCallback (IdlingResource.ResourceCallback callback): registra el IdlingResource.ResourceCallback dado con el recurso

Observaciones

Reglas de JUnit:

Como puede ver en el ejemplo de MockWebServer y en ActivityTestRule, todas se incluyen en la categoría de reglas JUnit que puede crear usted mismo, que luego deben ejecutarse para cada prueba que defina su comportamiento @see: https://github.com/junit-team/junit4/ wiki / rules

Apio

Parámetros

Dado que los parámetros tienen algunos problemas al colocarlos aquí hasta que se resuelva el error de documentación:

Parámetro Detalles
Actividad de clase clase que actividad comenzar
initialTouchMode en caso de que la actividad se coloque en modo táctil al inicio: https://android-developers.blogspot.de/2008/12/touch-mode.html
launchActivity Es cierto si la Actividad debe iniciarse una vez por método de prueba. Se lanzará antes del primer método Antes, y terminará después del último método Después.

Ejemplo de MockWebServer

En caso de que sus actividades, fragmentos e IU requieran algo de procesamiento en segundo plano, una buena cosa para usar es un MockWebServer que se ejecuta localmente en un dispositivo Android que brinda un entorno cerrado y comprobable para su IU.

https://github.com/square/okhttp/tree/master/mockwebserver

El primer paso es incluir la dependencia de Gradle:

testCompile 'com.squareup.okhttp3:mockwebserver:(insert latest version)'

Ahora los pasos para ejecutar y usar el servidor simulado son:

  • crear un objeto de servidor simulado
  • inícielo en la dirección y el puerto específicos (generalmente localhost: número de puerto)
  • Encolar respuestas para solicitudes específicas
  • comienza la prueba

Esto está muy bien explicado en la página github de mockwebserver pero en nuestro caso queremos algo mejor y reutilizable para todas las pruebas, y las reglas de JUnit entrarán en juego aquí:

/**
 *JUnit  rule that starts and stops a mock web server for test runner
*/
 public class MockServerRule extends UiThreadTestRule {

 private MockWebServer mServer;

 public static final int MOCK_WEBSERVER_PORT = 8000;

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                startServer();
                try {
                    base.evaluate();
                } finally {
                    stopServer();
                }
            }
        };
    }

    /**
     * Returns the started web server instance
     *
     * @return mock server
     */
    public MockWebServer server() {
        return mServer;
    }

    public void startServer() throws IOException, NoSuchAlgorithmException {
        mServer = new MockWebServer();
        try {
            mServer(MOCK_WEBSERVER_PORT);
        } catch (IOException e) {
            throw new IllegalStateException(e,"mock server start issue");
        }
    }

    public void stopServer() {
        try {
            mServer.shutdown();
        } catch (IOException e) {
            Timber.e(e, "mock server shutdown error”);
        }
    }
}

Ahora asumamos que tenemos exactamente la misma actividad que en el ejemplo anterior, solo en este caso cuando presionamos el botón, la aplicación buscará algo de la red, por ejemplo: https://someapi.com/name

Esto devolvería algunas cadenas de texto que se concatenarían en el texto de la barra de snack, por ejemplo, el NOMBRE + texto que ingresaste.

/**
* Testing of the snackbar activity with networking.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
    //espresso rule which tells which activity to start
    @Rule
    public final ActivityTestRule<SnackbarActivity> mActivityRule = 
        new ActivityTestRule<>(SnackbarActivity.class, true, false);

    //start mock web server
    @Rule
    public final MockServerRule mMockServerRule = new MockServerRule();

    @Override
    public void tearDown() throws Exception {
       //same as previous example
    }
    
    @Override
    public void setUp() throws Exception {
       //same as previous example

       **//IMPORTANT:** point your application to your mockwebserver endpoint e.g.
       MyAppConfig.setEndpointURL("http://localhost:8000");
    }
    
    /**
    *Test methods should always start with "testXYZ" and it is a good idea to 
    *name them after the intent what you want to test
    **/
    @Test
    public void testSnackbarIsShown() {
        //setup mockweb server
        mMockServerRule.server().setDispatcher(getDispatcher());

        mActivityRule.launchActivity(null);
        //check is our text entry displayed and enter some text to it
        String textToType="new snackbar text";
        onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
        //we check is our snackbar showing text from mock webserver plus the one we typed
        onView(withId(R.id.textEntry)).perform(typeText("JazzJackTheRabbit" + textToType));
        //click the button to show the snackbar
        onView(withId(R.id.shownSnackbarBtn)).perform(click());
        //assert that a view with snackbar_id with text which we typed and is displayed
        onView(allOf(withId(android.support.design.R.id.snackbar_text), 
        withText(textToType))) .check(matches(isDisplayed()));
    }
    
     /**
     *creates a mock web server dispatcher with prerecorded requests and responses
     **/
    private Dispatcher getDispatcher() {
        final Dispatcher dispatcher = new Dispatcher() {
            @Override
            public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
                if (request.getPath().equals("/name")){
                    return new MockResponse().setResponseCode(200)
                            .setBody("JazzJackTheRabbit");
                }
                throw new IllegalStateException("no mock set up for " + request.getPath());
            }
        };
        return dispatcher;
    }

Yo sugeriría envolver al despachador en una especie de Builder para que pueda encadenar fácilmente y agregar nuevas respuestas para sus pantallas. p.ej

 return newDispatcherBuilder()
            .withSerializedJSONBody("/authenticate", Mocks.getAuthenticationResponse())
            .withSerializedJSONBody("/getUserInfo", Mocks.getUserInfo())
            .withSerializedJSONBody("/checkNotBot", Mocks.checkNotBot());

IdlingResource

El poder de los recursos de inactividad radica en no tener que esperar a que el procesamiento de algunas aplicaciones (redes, cálculos, animaciones, etc.) finalice con el modo de sleep() , lo que trae la descamación y prolonga las pruebas. La documentación oficial se puede encontrar aquí .

Implementación

Hay tres cosas que debe hacer al implementar la interfaz IdlingResource :

  • getName() : devuelve el nombre de su recurso inactivo.
  • isIdleNow() : comprueba si su objeto xyz, operación, etc. está inactivo en este momento.
  • registerIdleTransitionCallback ( IdlingResource.ResourceCallback callback): proporciona una devolución de llamada que debe llamar cuando su objeto pase al estado inactivo.

Ahora debe crear su propia lógica y determinar cuándo su aplicación está inactiva y cuándo no, ya que esto depende de la aplicación. A continuación encontrará un ejemplo simple, solo para mostrar cómo funciona. Hay otros ejemplos en línea, pero la implementación de una aplicación específica lleva a implementaciones específicas de recursos inactivos.

NOTAS

  • Ha habido algunos ejemplos de Google donde pusieron IdlingResources en el código de la aplicación. No hagas esto. Presumiblemente lo colocaron allí solo para mostrar cómo funcionan.
  • ¡Mantener su código limpio y mantener un solo principio de responsabilidad depende de usted!

Ejemplo

Digamos que tiene una actividad que hace cosas extrañas y demora mucho tiempo en cargar el fragmento y, por lo tanto, hace que sus pruebas de Espresso fracasen al no poder encontrar recursos de su fragmento (debe cambiar cómo se crea su actividad y cuándo). para acelerarlo). Pero en cualquier caso, para mantenerlo simple, el siguiente ejemplo muestra cómo debería ser.

Nuestro ejemplo de recurso inactivo obtendría dos objetos:

  • La etiqueta del fragmento que necesita encontrar y en espera de unirse a la actividad.
  • Un objeto FragmentManager que se utiliza para encontrar el fragmento.
/**
 * FragmentIdlingResource - idling resource which waits while Fragment has not been loaded.
 */
public class FragmentIdlingResource implements IdlingResource {
    private final FragmentManager mFragmentManager;
    private final String mTag;
    //resource callback you use when your activity transitions to idle
    private volatile ResourceCallback resourceCallback;

    public FragmentIdlingResource(FragmentManager fragmentManager, String tag) {
        mFragmentManager = fragmentManager;
        mTag = tag;
    }

    @Override
    public String getName() {
        return FragmentIdlingResource.class.getName() + ":" + mTag;
    }

    @Override
    public boolean isIdleNow() {
        //simple check, if your fragment is added, then your app has became idle
        boolean idle = (mFragmentManager.findFragmentByTag(mTag) != null);
        if (idle) {
            //IMPORTANT: make sure you call onTransitionToIdle
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }
}

Ahora que tienes tu IdlingResource escrito, necesitas usarlo en algún lugar, ¿no?

Uso

Vamos a omitir la configuración completa de la clase de prueba y solo veremos cómo se vería un caso de prueba:

@Test
public void testSomeFragmentText() {
    mActivityTestRule.launchActivity(null);
   
    //creating the idling resource
    IdlingResource fragmentLoadedIdlingResource = new FragmentIdlingResource(mActivityTestRule.getActivity().getSupportFragmentManager(), SomeFragmentText.TAG);
    //registering the idling resource so espresso waits for it
    Espresso.registerIdlingResources(idlingResource1);
    onView(withId(R.id.txtHelloWorld)).check(matches(withText(helloWorldText)));

    //lets cleanup after ourselves
    Espresso.unregisterIdlingResources(fragmentLoadedIdlingResource);
}

Combinación con regla JUnit

Esto no es difícil; También puede aplicar el recurso inactivo en forma de una regla de prueba JUnit. Por ejemplo, digamos que tiene algún SDK que contiene Volley y quiere que Espresso lo espere. En lugar de pasar por cada caso de prueba o aplicarlo en la configuración, puede crear una regla JUnit y simplemente escribir:

@Rule
public final SDKIdlingRule mSdkIdlingRule = new SDKIdlingRule(SDKInstanceHolder.getInstance());

Ahora, ya que este es un ejemplo, no lo des por sentado; Todo el código aquí es imaginario y se usa solo para fines de demostración:

public class SDKIdlingRule implements TestRule {
    //idling resource you wrote to check is volley idle or not
    private VolleyIdlingResource mVolleyIdlingResource;
    //request queue that you need from volley to give it to idling resource
    private RequestQueue mRequestQueue;

    //when using the rule extract the request queue from your SDK
    public SDKIdlingRule(SDKClass sdkClass) {
        mRequestQueue = getVolleyRequestQueue(sdkClass);
    }

    private RequestQueue getVolleyRequestQueue(SDKClass sdkClass) {
        return sdkClass.getVolleyRequestQueue();
    }

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                //registering idling resource
                mVolleyIdlingResource = new VolleyIdlingResource(mRequestQueue);
                Espresso.registerIdlingResources(mVolleyIdlingResource);
                try {
                    base.evaluate();
                } finally {
                    if (mVolleyIdlingResource != null) {
                        //deregister the resource when test finishes
                        Espresso.unregisterIdlingResources(mVolleyIdlingResource);
                    }
                }
            }
        };
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow