The Android Developer Challenge is back! Submit your idea before December 2.

Espresso-Intents

Espresso-Intents é uma extensão do Espresso, que permite validar e criar stubs de intents enviados pelo aplicativo em teste. É como o Mockito, mas para intents do Android.

Se seu app delegar recursos a outros apps ou à plataforma, você poderá usar o Espresso-Intents para se concentrar na lógica do próprio app, contanto que outros apps ou a plataforma funcionem corretamente. Com o Espresso-Intents, você pode combinar e validar seus intents de saída ou inclusive disponibilizar respostas de stub em vez de respostas de intent reais.

Incluir o Espresso-Intents no projeto

No arquivo app/build.gradle do app, adicione a seguinte linha dentro das dependencies:

androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'
    

O Espresso-Intents é compatível apenas com o Espresso 2.1 e posterior e com versões das bibliotecas de testes do Android posteriores à 0.3. Portanto, atualize estas linhas também:

androidTestImplementation 'androidx.test:runner:1.1.0'
    androidTestImplementation 'androidx.test:rules:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
    

Programar regras de teste

Antes de programar um teste do Espresso-Intents, configure uma IntentsTestRule. Essa é uma extensão da classe ActivityTestRule que facilita o uso de APIs do Espresso-Intents em testes funcionais da IU. Uma IntentsTestRule inicializa o Espresso-Intents antes de cada teste anotado com @Test e libera o Espresso-Intents depois de cada teste executado.

O snippet de código a seguir é um exemplo de IntentsTestRule:

Kotlin

    @get:Rule
    val intentsTestRule = IntentsTestRule(MyActivity::class.java)
    

Java

    @Rule
    public IntentsTestRule<MyActivity> intentsTestRule =
        new IntentsTestRule<>(MyActivity.class);
    

Correspondência

O Espresso-Intents permite interceptar os intents de saída com base em certos critérios de correspondência, que são definidos com o uso de matchers do Hamcrest. O Hamcrest permite:

  • usar um matcher de intent existente: opção mais fácil, quase sempre preferencial;
  • implementar o próprio matcher de intent: opção mais flexível. Mais detalhes estão disponíveis na seção "Programar matchers personalizados" no Tutorial do Hamcrest (em inglês).

O Espresso-Intents oferece os métodos intended() e intending() para validar e criar stubs de intents, respectivamente. Ambos usam um objeto Matcher<Intent> do Hamcrest como argumento.

O snippet de código a seguir mostra uma validação que usa matchers de intent existentes. Esses matchers associam um intent de saída que inicia um navegador:

Kotlin

    assertThat(intent).hasAction(Intent.ACTION_VIEW)
    assertThat(intent).categories().containsExactly(Intent.CATEGORY_BROWSABLE)
    assertThat(intent).hasData(Uri.parse("www.google.com"))
    assertThat(intent).extras().containsKey("key1")
    assertThat(intent).extras().string("key1").isEqualTo("value1")
    assertThat(intent).extras().containsKey("key2")
    assertThat(intent).extras().string("key2").isEqualTo("value2")
    

Java

    assertThat(intent).hasAction(Intent.ACTION_VIEW);
    assertThat(intent).categories().containsExactly(Intent.CATEGORY_BROWSABLE);
    assertThat(intent).hasData(Uri.parse("www.google.com"));
    assertThat(intent).extras().containsKey("key1");
    assertThat(intent).extras().string("key1").isEqualTo("value1");
    assertThat(intent).extras().containsKey("key2");
    assertThat(intent).extras().string("key2").isEqualTo("value2");
    

Validar intents

O Espresso-Intents registra todos os intents que tentam iniciar atividades no aplicativo em teste. Usando o método intended(), que é semelhante a Mockito.verify(), você pode afirmar que um determinado intent foi visto. No entanto, o Espresso-Intents não cria stubs de respostas para intents, a menos que você o configure explicitamente para fazer isso.

O snippet de código a seguir é um exemplo de teste que valida respostas, mas não cria stubs delas, para um intent de saída que inicia uma atividade "phone" externa:

Kotlin

    @Test fun validateIntentSentToPackage() {
        // User action that results in an external "phone" activity being launched.
        user.clickOnView(system.getView(R.id.callButton))

        // Using a canned RecordedIntentMatcher to validate that an intent resolving
        // to the "phone" activity has been sent.
        intended(toPackage("com.android.phone"))
    }
    

Java

    @Test
    public void validateIntentSentToPackage() {
        // User action that results in an external "phone" activity being launched.
        user.clickOnView(system.getView(R.id.callButton));

        // Using a canned RecordedIntentMatcher to validate that an intent resolving
        // to the "phone" activity has been sent.
        intended(toPackage("com.android.phone"));
    }
    

Criar stubs

Usando o método intending(), que é semelhante a Mockito.when(), você pode oferecer uma resposta de stub para atividades que são iniciadas com startActivityForResult(). Isso é particularmente útil para atividades externas, porque não é possível manipular a interface do usuário de uma atividade externa nem controlar o ActivityResult retornado para a atividade em teste.

Os seguintes snippets de código implementam um teste activityResult_DisplaysContactsPhoneNumber() de exemplo, que verifica se o número de telefone do contato é exibido quando o usuário inicia uma atividade "contact" no app em teste:

  1. Crie o resultado a ser retornado quando uma atividade específica for iniciada. O teste de exemplo intercepta todos os intents enviados para "contacts" e apaga as respostas relacionadas com um ActivityResult válido, usando o código de resultado RESULT_OK.

    Kotlin

        val resultData = Intent()
        val phoneNumber = "123-345-6789"
        resultData.putExtra("phone", phoneNumber)
        val result = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)
        

    Java

        Intent resultData = new Intent();
        String phoneNumber = "123-345-6789";
        resultData.putExtra("phone", phoneNumber);
        ActivityResult result =
            new ActivityResult(Activity.RESULT_OK, resultData);
        
  2. Instrua o Espresso a disponibilizar o objeto de resultado de stub em resposta a todas as invocações do intent "contacts":

    Kotlin

        intending(toPackage("com.android.contacts")).respondWith(result)
        

    Java

        intending(toPackage("com.android.contacts")).respondWith(result);
        
  3. Verifique se a ação usada para iniciar a atividade produz o resultado de stub esperado. Nesse caso, o teste de exemplo verifica se o número de telefone "123-345-6789" é retornado e exibido quando a atividade "contacts" é iniciada:

    Kotlin

        onView(withId(R.id.pickButton)).perform(click())
        onView(withId(R.id.phoneNumber)).check(matches(withText(phoneNumber)))
        

    Java

        onView(withId(R.id.pickButton)).perform(click());
        onView(withId(R.id.phoneNumber)).check(matches(withText(phoneNumber)));
        

Este é o teste activityResult_DisplaysContactsPhoneNumber() completo:

Kotlin

    @Test fun activityResult_DisplaysContactsPhoneNumber() {
        // Build the result to return when the activity is launched.
        val resultData = Intent()
        val phoneNumber = "123-345-6789"
        resultData.putExtra("phone", phoneNumber)
        val result = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)

        // Set up result stubbing when an intent sent to "contacts" is seen.
        intending(toPackage("com.android.contacts")).respondWith(result)

        // User action that results in "contacts" activity being launched.
        // Launching activity expects phoneNumber to be returned and displayed.
        onView(withId(R.id.pickButton)).perform(click())

        // Assert that the data we set up above is shown.
        onView(withId(R.id.phoneNumber)).check(matches(withText(phoneNumber)))
    }
    

Java

    @Test
    public void activityResult_DisplaysContactsPhoneNumber() {
        // Build the result to return when the activity is launched.
        Intent resultData = new Intent();
        String phoneNumber = "123-345-6789";
        resultData.putExtra("phone", phoneNumber);
        ActivityResult result =
            new ActivityResult(Activity.RESULT_OK, resultData);

        // Set up result stubbing when an intent sent to "contacts" is seen.
        intending(toPackage("com.android.contacts")).respondWith(result);

        // User action that results in "contacts" activity being launched.
        // Launching activity expects phoneNumber to be returned and displayed.
        onView(withId(R.id.pickButton)).perform(click());

        // Assert that the data we set up above is shown.
        onView(withId(R.id.phoneNumber)).check(matches(withText(phoneNumber)));
    }
    

Outros recursos

Para saber mais sobre o uso do Espresso-Intents em testes do Android, consulte os recursos a seguir.

Exemplos