Exemplo de testes de autoinstrumentação

Quando um teste de instrumentação é iniciado, o pacote de destino é reiniciado com o código de instrumentação injetado e iniciado para execução. Um mas o pacote de destino não pode ser o aplicativo Android próprio framework, como o pacote android, porque isso leva ao situação paradoxal em que o framework do Android precisaria ser reiniciado, o que é o que dá suporte às funções do sistema, incluindo a própria instrumentação.

Isso significa que um teste de instrumentação não pode ser injetado no framework do Android, também conhecido como servidor do sistema, para execução. Para testar o Android o código de teste poderá invocar somente superfícies de API públicas ou aquelas usando a Linguagem de definição de interface do Android AIDL (em inglês) disponíveis na árvore de origem da plataforma. Para essa categoria de testes, não é significativo segmentar nenhum pacote específico. Portanto, é comum instrumentações sejam declaradas como alvo do próprio pacote de aplicativo de teste, conforme definido na própria tag <manifest> de AndroidManifest.xml.

Dependendo dos requisitos, os pacotes de teste de aplicativos nessa categoria também podem fazer o seguinte:

  • Agrupar atividades necessárias para testes.
  • Compartilhe o ID do usuário com o sistema.
  • Ser assinado com a chave da plataforma.
  • ser compilado na fonte do framework em vez de no SDK público;

Essa categoria de testes de instrumentação às vezes é chamada de autoinstrumentação. Confira alguns exemplos de testes de autoinstrumentação na fonte da plataforma:

O exemplo abordado aqui é criar um novo teste de instrumentação com é definido no próprio pacote de aplicativo de teste. Neste guia, usamos o seguinte: como exemplo:

É recomendável navegar pelo código primeiro para ter uma boa impressão. antes de continuar.

Escolher um local de origem

Normalmente, sua equipe já terá um padrão estabelecido de lugares para verificar no código e locais para adicionar testes. A maioria das equipes tem um único repositório Git ou compartilhar um com outras equipes, mas ter um subdiretório dedicado que contém o código-fonte do componente.

Supondo que o local raiz da origem do componente esteja em <component source root>, a maioria dos componentes tem pastas src e tests abaixo dele e alguns arquivos adicionais, como Android.mk (ou divididos em outros arquivos .mk), o arquivo de manifesto AndroidManifest.xml e o arquivo de configuração de teste 'AndroidTest.xml'.

Como você está adicionando um novo teste, provavelmente será necessário criar o tests ao lado do componente src e o preencha com conteúdo.

Em alguns casos, sua equipe pode ter mais estruturas de diretórios em tests. devido à necessidade de empacotar diferentes conjuntos de testes em APKs individuais. E Nesse caso, é necessário criar um novo subdiretório em tests.

Independentemente da estrutura, você preencherá o diretório tests ou no subdiretório recém-criado, com arquivos semelhantes ao que instrumentation na mudança do Gerrit de amostra. Os detalhes de cada são explicados mais adiante neste documento.

Arquivo de manifesto

Como acontece com um projeto de aplicativo, cada módulo de teste de instrumentação requer uma arquivo de manifesto chamado AndroidManifest.xml. Para incluir automaticamente este arquivo usando o makefile principal BUILD_PACKAGE. Forneça esse arquivo ao lado do Android.mk para seu módulo de teste.

Se você não conhece o arquivo AndroidManifest.xml, consulte a Visão geral do manifesto do app

Confira a seguir um exemplo de arquivo AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  android:sharedUserId="android.uid.system"
  package="android.test.example.helloworld" >

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

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="android.test.example.helloworld"
                     android:label="Hello World Test"/>

</manifest>

Algumas observações selecionadas no arquivo de manifesto:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.test.example.helloworld" >

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

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

Embora o nome do pacote geralmente esteja no mesmo estilo, como um nome de pacote Java, ele tem poucas coisas a ver com ele. Em outras palavras, o pacote do aplicativo (ou teste) pode conter classes com qualquer nome de pacote. Por outro lado, você pode 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.

android:sharedUserId="android.uid.system"

Isso declara que, no momento da instalação, esse arquivo APK precisa receber o mesmo ID do usuário, ou seja, a identidade de execução, como a plataforma principal. Isso depende do APK ser assinado com o mesmo certificado da plataforma principal (consulte LOCAL_CERTIFICATE em uma seção anterior), mas são conceitos diferentes:

  • algumas permissões ou APIs são protegidas por assinatura, o que exige a certificado de assinatura
  • algumas permissões ou APIs exigem a identidade de usuário system do autor da chamada; que exige que o pacote de chamada compartilhe o ID do usuário com system, se for um pacote separado da plataforma principal
<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 de biblioteca JAR de framework separado, exigindo, portanto, entradas do caminho de classe quando o pacote de teste é invocado pela estrutura do aplicativo.

android:targetPackage="android.test.example.helloworld"

Você pode ter notado que a targetPackage é declarada da mesma forma que a Atributo package declarado na tag manifest desse arquivo. Como mencionado no noções básicas de teste, essa categoria de teste de instrumentação é é normalmente destinado ao teste de APIs de framework, por isso não é muito significativo para que tenham um pacote de aplicativos de destino específico, diferente do próprio pacote.

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 de módulos, dependências de tempo de compilação e empacotamento instruções. Na maioria dos casos, a opção de arquivo de blueprint baseada em Soong é suficiente. Para mais detalhes, consulte Configuração de teste simples.

Arquivo de configuração complexo

Nesses casos mais complexos, você também precisa criar uma configuração de teste para o arcabouço de testes do Android, Trade Federation (link em inglês).

A configuração de teste pode especificar opções especiais de configuração do dispositivo e para fornecer a classe de teste. Veja o exemplo em /platform_testing/tests/example/instrumentation/AndroidTest.xml.

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

<configuration description="Runs sample instrumentation test.">
  <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
  <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
    <option name="test-file-name" value="HelloWorldTests.apk"/>
  </target_preparer>
  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
  <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
  <option name="test-suite-tag" value="apct"/>
  <option name="test-tag" value="SampleInstrumentationTest"/>

  <test class="com.android.tradefed.testtype.AndroidJUnitTest">
    <option name="package" value="android.test.example.helloworld"/>
    <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="HelloWorldTests.apk"/>
</target_preparer>

Isso informa à Trade Federation para instalar o HelloWorldTests.apk no alvo. usando um target_preparer especificado. Há muitos preparadores de destino disponíveis para desenvolvedores na Trade Federation e podem ser usados para garantir o dispositivo está configurado corretamente antes da execução do teste.

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

Isso especifica a classe de teste da Trade Federation a ser usada para executar o teste e passa o pacote no dispositivo a ser executado, e o executor de testes que é o JUnit neste caso.

Para mais informações, consulte Configurações do módulo de teste.

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. O exemplo de alteração do gerrit contém algumas uso de seus recursos. Veja o exemplo em /platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java.

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

@RunWith(JUnit4.class)
public class HelloWorldTest {

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ê cria testes em classes Java simples e usa a anotação para indicar determinadas configurações e restrições de teste. Em exemplo, instruímos que essa classe precisa ser executada como um teste do JUnit4.

    @BeforeClass
    public static void beforeClass() {
    ...
    @AfterClass
    public static void afterClass() {
    ...
    @Before
    public void before() {
    ...
    @After
    public void after() {
    ...
    @Test
    @SmallTest
    public void testHelloWorld() {
    ...

As anotações @Before e @After são usadas em métodos do JUnit4 para realizar configuração pré-teste e desmontagem pós-teste. Da mesma forma, os métodos @BeforeClass e As anotações @AfterClass são usadas em métodos do JUnit4 para realizar a configuração antes executar todos os testes em uma classe de teste e realizar a desmontagem posteriormente. Observe que o Os métodos de configuração e desmontagem do escopo da classe precisam ser estáticos. Quanto aos métodos de teste, diferente 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 nenhum valor de retorno, não ter parâmetros e pode gerar exceções.

Acesso à classe de instrumentação

Embora não seja abordado no exemplo básico do Hello World, é bastante comum que um Teste do Android para exigir acesso à instância Instrumentation: esta é a API principal interface que fornece acesso a contextos do aplicativo, ciclo de vida da atividade APIs de teste relacionadas e muito mais.

Como os testes do JUnit4 não precisam mais de uma classe base comum, não é mais necessário acessar a instância Instrumentation usando InstrumentationTestCase#getInstrumentation(). Em vez disso, o novo executor de teste a gerencia usando InstrumentationRegistry, onde a configuração contextual e ambiental criada pelo framework de instrumentação é armazenada.

Para acessar a instância da classe Instrumentation, basta chamar o método estático getInstrumentation() na classe InstrumentationRegistry:

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

Criar e testar localmente

Para os casos de uso mais comuns, use Atest:

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