Automatizamos testes de interface do usuário de aplicativos Android usando o padrão Objeto de Página

imagem

O padrão Objeto de Página apareceu nos testes da Web e provou ser muito bom lá. Quando comecei a automatizar testes para aplicativos Android, a primeira coisa que pensei sobre isso. Procurei informações na rede, perguntei aos meus colegas e, em princípio, não encontrei motivos para não tentar. Proponho ver o que resultou disso.

O objeto de página clássico implica dois níveis de abstração: elementos da página e testes. Destaco mais uma - lógica de negócios. Observo que a maneira como você constrói sua estrutura afetará enormemente a facilidade de escrever testes no futuro, bem como o suporte deles. Tento fazer com que o código de teste pareça um caso de teste normal, escrito por um testador comum. I.e. Eu começo do final:

  1. Estou escrevendo um código de caso de teste bonito e claro,
  2. Eu implemento métodos do caso de teste na camada de lógica de negócios,
  3. Descrevo os elementos necessários para o teste.

Essa abordagem é boa porque não fazemos nada extra - a estrutura é construída o quanto for necessário para os testes funcionarem. Podemos dizer que esse é o conceito de MVP nos testes: eles rapidamente formaram uma peça e ela já começou a trazer benefícios. Se você primeiro escrever milhares de linhas, descrevendo as páginas do seu aplicativo e como interagir com elas, e depois de três meses sair do buraco para o primeiro clique e perceber que tinha que fazer tudo de maneira diferente, a maior parte da sua criação estará condenada para sempre " reunir poeira "nos porões do git ... Um verdadeiro testador sabe que quanto mais cedo um bug é encontrado, mais barato é corrigi-lo. Use essa abordagem em tudo - escreveu um teste em algumas horas, experimentou, não gostou - jogou fora, aprendeu uma lição, seguiu em frente.

Portanto, a entrada é:

  • Aplicativo Android para negociação na bolsa de valores;
  • Java para trabalhar na mesma pilha com desenvolvedores;
  • estrutura básica UI Automator;
  • você precisa escrever um teste de login no aplicativo.

Preparação do projeto


Desde que tentei integrar o máximo possível ao processo de desenvolvimento, não comecei a criar um novo projeto. De acordo com a documentação , os testes instrumentais devem ser colocados na pasta src/androidTest/java . No meu caso, o coletor já foi configurado; se você tiver algo errado, leia sobre a configuração da compilação . Também precisamos de um ambiente de desenvolvimento, Android SDK, Emulador, ferramentas, ferramentas de plataforma e as plataformas necessárias para o emulador. Se você usa o Android Studio, tudo isso pode ser instalado rapidamente através do SDK Manager:



Camada de teste


Como escrevi acima, automatizaremos o teste de login no aplicativo. Lembre-se de que o código deve se parecer com um caso de teste regular:

Pré-condição: execute o aplicativo.
Etapa 1: faça login usando a conta myLogin / myPassword.
Etapa 2: verifique o nome do usuário atual.
Resultado esperado: o usuário atual é Ivan Ivanov.

Isenção de responsabilidade pequena: de acordo com as melhores práticas do teste de design, na pré-condição em que você precisa criar / encontrar uma conta. Omiti esse momento por simplicidade de exemplo.

Nossa classe ficará assim:

 @RunWith(AndroidJUnit4.class) public class LoginTests { private TestApplication myApp; @Before public void setUp() { myApp = new TestApplication(); } @Test public void testLogin() { myApp.login("myLogin","myPassword"); String currentUser = myApp.getCurrentUserName(); assertEquals("Wrong name", " ", currentUser); } @After public void tearDown() { myApp.close(); } } 

Lógica de negócios


O teste usa a classe TestApplication() e seus dois métodos: login() e getCurrentUserName() . Além disso, você precisa de um construtor de classe (iniciando o aplicativo) e do método close() .A frente é clara:

 public class TestApplication { private UiDevice device; private PageObject page; public TestApplication() { } public void login(String login, String password) { } public String getCurrentUserName() { return "" } public void close() { } } 

Uma instância da nossa classe terá duas variáveis:

  • device , pertencente à classe android.support.test.uiautomator.UiDevice - através dele, interagimos com o dispositivo;
  • page , a classe PageObject , que criaremos na próxima seção.

Vamos começar com o construtor. Nele, crie instâncias de nossas variáveis ​​e execute o aplicativo:

 public TestApplication() { // Connect to device device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); device.pressHome(); // Get launch intent String packageName = InstrumentationRegistry.getTargetContext() .getPackageName(); Context context = InstrumentationRegistry.getContext(); Intent intent = context.getPackageManager() .getLaunchIntentForPackage(packageName) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Stat application context.startActivity(intent); // Get page objects page = new PageObject(device, packageName); } 

Algumas dicas sobre como iniciar o aplicativo
Para ter mais controle do aplicativo em teste, você pode adicionar expectativas para o iniciador (se você executar o emulador "em um frio") e o próprio aplicativo. Isso também é recomendado na documentação .

Nota: a execução do aplicativo por android.content.Context.getTargetContext() será adequada apenas se seus testes estiverem no mesmo projeto que o próprio aplicativo. Se separadamente, será necessário percorrer o menu.

A lógica de negócios do teste são as etapas específicas que o usuário deve executar para obter qualquer resultado significativo (para o usuário). O login com seu próprio ultrassom é um resultado significativo. Etapas: clique no botão "Login", digite o nome de usuário no campo "Login", digite a senha no campo "Senha", clique no botão "Entrar". Assim, nosso método é preenchido com etapas:

 public void login(String login, String password) { page.login().click(); page.loginEntry().setText(login); page.passwordEntry().setText(password); page.signIn().click(); } 

Para obter o usuário atual, tudo é mais simples, basta obter o valor do campo:

 public String getCurrentUserName() { return page.currentUserName().getText(); } } 

E para fechar o aplicativo, basta clicar no botão Início:

 public void close() { device.pressHome(); } 

Descrição dos elementos da página


O conceito dessa camada é que ela retorne elementos prontos para uso (em nosso contexto, essa é a classe android.support.test.uiautomator.UiObject2 ). Ou seja, o consumidor não deve se preocupar com o estado do objeto, se ele retornar, você poderá interagir imediatamente com ele: clique, preencha ou leia o texto. Essa é uma consequência importante - nesta camada, perceberemos as expectativas:

 private UiObject2 getUiObject(BySelector selector) { return device.wait(Until.findObject(selector), 10000); } 

O método definido acima será usado pela interface pública da nossa classe PageObject . Exemplo para o campo Login:

 public UiObject2 loginEntry() { return getUiObject(loginEntrySelector()); } 

Resta determinar o seletor pelo qual encontraremos o campo. A instância do android.support.test.uiautomator.BySelector mais convenientemente obtida usando os métodos estáticos da classe android.support.test.uiautomator.By . Concordei com o desenvolvimento de que em todos os lugares, sempre que possível, haverá um ID de recurso exclusivo:

 private BySelector loginEntrySelector() { return By.res(packageName, "login"); } 

É conveniente inspecionar a interface no utilitário uiautomatorviewer incluído no pacote de ferramentas (instalado na seção Preparação):



Selecione o elemento desejado e, na seção Detalhes do nó, examinamos o ID do recurso. Consiste no nome do pacote e, de fato, no identificador. Obtivemos o nome do pacote no construtor da classe TestApplication e o usamos ao criar o PageObject .

Código completo:

classe PageObject
 public class PageObject { private UiDevice device; private final String packageName; private BySelector loginButtonSelector() { return By.res(packageName, "user_view"); } private BySelector loginEntrySelector() { return By.res(packageName, "login"); } private BySelector passwordEntrySelector() { return By.res(packageName, "password"); } private BySelector signInSelector() { return By.res(packageName, "btnLogin"); } private BySelector userNameSelector() { return By.res(packageName, "user_name"); } public PageObject(UiDevice device, String packageName) { this.device = device; this.packageName = packageName; } public UiObject2 login() { return getUiObject(loginButtonSelector()); } public UiObject2 loginEntry() { return getUiObject(loginEntrySelector()); } public UiObject2 passwordEntry() { return getUiObject(passwordEntrySelector()); } public UiObject2 signIn() { return getUiObject(signInSelector()); } public UiObject2 currentUserName() { return getUiObject(userNameSelector()); } private UiObject2 getUiObject(BySelector selector) { return device.wait(Until.findObject(selector), 10000); } } 


Executando testes


Isso é tudo, resta correr. Primeiro, conecte-se ao dispositivo ou inicie o emulador. Verifique a conexão com o comando adb devices (o utilitário adb faz parte das ferramentas de plataforma):

 List of devices attached emulator-5554 device 

Se você tem um gradle, faça

 gradlew.bat connectedAndroidTest 

e aprecie como "robôs injetados, não humanos" (c).

Source: https://habr.com/ru/post/pt416397/


All Articles