Segmentar um exemplo de app

Essa categoria de teste de instrumentação não é muito diferente daquelas destinadas aos aplicativos Android normais. O aplicativo de teste que incluiu a instrumentação precisa ser assinado com o mesmo certificado do aplicativo de destino.

Neste guia, presumimos que você já tenha algum conhecimento do fluxo de trabalho da árvore de origem da plataforma. Caso contrário, consulte Requisitos. O exemplo abordado aqui é a criação de um novo teste de instrumentação com o pacote de destino definido no próprio pacote do aplicativo de teste. Se você não estiver familiarizado com o conceito, leia a Introdução aos testes de plataforma.

Este guia usa o teste a seguir como exemplo:

  • frameworks/base/packages/Shell/tests

Recomendamos navegar pelo código primeiro para ter uma ideia geral antes de prosseguir.

Decidir um local de origem

Como o teste de instrumentação será destinado a um aplicativo, a convenção é colocar o código-fonte do teste em um diretório tests na raiz do diretório de origem do componente na árvore de origem da plataforma.

Confira mais discussões sobre o local de origem no exemplo de ponta a ponta para testes de auto-instrumentação.

Arquivo de manifesto

Assim como um aplicativo normal, cada módulo de teste de instrumentação precisa de um arquivo de manifesto. Se você nomear o arquivo como AndroidManifest.xml e fornecê-lo ao lado de Android.mk para o módulo de teste, ele será incluído automaticamente pelo makefile principal BUILD_PACKAGE.

Antes de prosseguir, recomendamos que você leia a Visão geral do manifesto do app.

Isso oferece uma visão geral dos componentes básicos de um arquivo de manifesto e das funcionalidades deles.

A versão mais recente do arquivo de manifesto para a mudança de gerrit de exemplo pode ser acessada em: https://android.googlesource.com/platform/frameworks/base/+/android17-release/packages/Shell/tests/AndroidManifest.xml (link em inglês)

Um snapshot é incluído aqui para sua conveniência:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.shell.tests">

    <application>
        <uses-library android:name="android.test.runner" />

        <activity
            android:name="com.android.shell.ActionSendMultipleConsumerActivity"
            android:label="ActionSendMultipleConsumer"
            android:theme="@android:style/Theme.NoDisplay"
            android:noHistory="true"
            android:excludeFromRecents="true">
            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>
        </activity>
    </application>

    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
        android:targetPackage="com.android.shell"
        android:label="Tests for Shell" />

</manifest>

Algumas observações selecionadas sobre o arquivo de manifesto:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.shell.tests">

O atributo package é o nome do pacote do aplicativo: esse é o identificador exclusivo que o framework do app Android usa para identificar um aplicativo (ou, neste contexto, o aplicativo de teste). Cada usuário no sistema só pode instalar um aplicativo com esse nome de pacote.

Como esse é um pacote de aplicativo de teste, independente do pacote do aplicativo em teste, um nome de pacote diferente precisa ser usado: uma convenção comum é adicionar um sufixo .test.

Além disso, esse package atributo é o mesmo que ComponentName#getPackageName() retorna e também o mesmo que você usaria para interagir com vários comandos secundários pm via adb shell.

Embora o nome do pacote seja normalmente no mesmo estilo de um nome de pacote Java, ele tem muito pouco a ver com isso. Em outras palavras, o pacote do aplicativo (ou de teste) pode conter classes com qualquer nome de pacote, embora, por outro lado, você possa optar pela simplicidade e ter o nome do pacote Java de nível superior no aplicativo ou teste idêntico ao nome do pacote do aplicativo.

<uses-library android:name="android.test.runner" />

Isso é necessário para todos os testes de instrumentação, já que as classes relacionadas são empacotadas em um arquivo jar de framework separado e, portanto, exigem entradas de caminho de classe adicionais quando o pacote de teste é invocado pelo framework do aplicativo.

android:targetPackage="com.android.shell"

Isso define o pacote de destino da instrumentação como com.android.shell. Quando a instrumentação é invocada pelo comando am instrument, o framework reinicia o processo com.android.shell e injeta o código de instrumentação no processo para execução do teste. Isso também significa que o código de teste terá acesso a todas as instâncias de classe em execução no aplicativo em teste e poderá manipular o estado dependendo dos hooks de teste expostos.

Arquivo de configuração simples

Cada novo módulo de teste precisa ter um arquivo de configuração para direcionar o sistema de build com metadados do módulo, dependências de tempo de compilação e instruções de empacotamento. Na maioria dos casos, a opção de arquivo do Blueprint baseado no Soong é suficiente. Consulte Configuração de teste simples para mais detalhes.

Arquivo de configuração complexo

Para testes mais complexos, também é necessário gravar um arquivo de configuração de teste para o harness de teste do Android, o Trade Federation.

A configuração de teste pode especificar opções especiais de configuração do dispositivo e argumentos padrão para fornecer a classe de teste.

A versão mais recente do arquivo de configuração para a mudança de gerrit de exemplo pode ser acessada em: frameworks/base/packages/Shell/tests/AndroidTest.xml (link em inglês)

Um snapshot é incluído aqui para sua conveniência:

<configuration description="Runs Tests for Shell.">
    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
        <option name="test-file-name" value="ShellTests.apk" />
    </target_preparer>

    <option name="test-suite-tag" value="apct" />
    <option name="test-tag" value="ShellTests" />
    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
        <option name="package" value="com.android.shell.tests" />
        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
    </test>
</configuration>

Algumas observações selecionadas sobre o arquivo de configuração de teste:

<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
  <option name="test-file-name" value="ShellTests.apk"/>
</target_preparer>

Isso informa ao Trade Federation para instalar o ShellTests.apk no dispositivo de destino usando um target_preparer especificado. Há muitos preparadores de destino disponíveis para desenvolvedores no Trade Federation, e eles podem ser usados para garantir que o dispositivo esteja configurado corretamente antes da execução do teste.

<test class="com.android.tradefed.testtype.AndroidJUnitTest">
  <option name="package" value="com.android.shell.tests"/>
  <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
</test>

Isso especifica a classe de teste do Trade Federation a ser usada para executar o teste e transmite o pacote no dispositivo a ser executado e o framework do executor de testes, que é o JUnit nesse caso.

Confira mais informações sobre as configurações do módulo de teste (link em inglês).

Recursos do JUnit4

O uso da biblioteca android-support-test como executor de testes permite a adoção de novas classes de teste no estilo JUnit4, e a mudança de gerrit de exemplo contém alguns usos muito básicos dos recursos.

O código-fonte mais recente da mudança de gerrit de exemplo pode ser acessado em: frameworks/base/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java (link em inglês)

Embora os padrões de teste sejam geralmente específicos para equipes de componentes, há alguns padrões de uso geralmente úteis.

@SmallTest
@RunWith(AndroidJUnit4.class)
public final class FeatureFactoryImplTest {

Uma diferença significativa no JUnit4 é que os testes não precisam mais herdar de uma classe de teste base comum. Em vez disso, você grava testes em classes Java simples e usa anotações para indicar determinadas configurações e restrições de teste. Neste exemplo, estamos instruindo que essa classe seja executada como um teste do Android JUnit4.

A anotação @SmallTest especificou um tamanho de teste para toda a classe de teste: todos os métodos de teste adicionados a essa classe herdam essa anotação de tamanho de teste. configuração da classe de pré-teste, desmontagem pós-teste e desmontagem da classe pós-teste: semelhante aos métodos setUp e tearDown no JUnit4. A anotação Test é usada para anotar o teste real.

    @Before
    public void setup() {
    ...
    @Test
    public void testGetProvider_shouldCacheProvider() {
    ...

A anotação @Before é usada em métodos pelo JUnit4 para realizar a configuração de pré-teste. Embora não seja usada neste exemplo, também há @After para desmontagem pós-teste. Da mesma forma, as anotações @BeforeClass e @AfterClass podem ser usadas em métodos pelo JUnit4 para realizar a configuração antes de executar todos os testes em uma classe de teste e a desmontagem depois. Observação: os métodos de configuração e desmontagem do escopo da classe precisam ser estáticos.

Quanto aos métodos de teste, ao contrário da versão anterior do JUnit, eles não precisam mais iniciar o nome do método com test. Em vez disso, cada um deles precisa ser anotado com @Test. Como de costume, os métodos de teste precisam ser públicos, não declarar valor de retorno, não receber parâmetros e podem gerar exceções.

        Context context = InstrumentationRegistry.getTargetContext();

Como os testes do JUnit4 não exigem mais uma classe de base comum, não é mais necessário receber Context instâncias via getContext() ou getTargetContext() usando métodos de classe de base. Em vez disso, o novo executor de testes os gerencia via InstrumentationRegistry em que a configuração contextual e ambiental criada pelo framework de instrumentação é armazenada. Nessa classe, também é possível chamar:

  • getInstrumentation(): a instância da classe Instrumentation
  • getArguments(): os argumentos da linha de comando transmitidos para am instrument via -e <key> <value>

Criar e testar localmente

Para os casos de uso mais comuns, use o Atest.

Para casos mais complexos que exigem personalização mais pesada, siga as instruções de instrumentação.