Recentemente, fiz um projeto para efetuar testes em dispositivos
Android utilizando a linguagem
Python. Esses testes eram feitos em um celular da Samsung conectado via USB a um computador com Windows. Os dispositivos era testados utilizando um comando chamado
adb (Android Debug Bridge).
Todos os meus outros posts foram feitos utilizando
Linux, esse eu vou fazer utilizando o Windows, pois quero replicar o mesmo ambiente do cliente e mostrar que o Python também roda muito bem em ambientes Windows.
O primeiro passo é instalar um "cara" chamado
Android Studio, pois nele já tem o SDKmanager, o
adb e todos os requisitos necessários para o desenvolvimento Android.
Para instalar, é só baixar no site oficial:
Após fazer a instalação eu coloquei o caminho do
adb dentro da variável PATH do Windows:
→ C:\Users\alisson\AppData\Local\Android\sdk\platform-tools
Não entendo nada de Windows, mas na versão 10 é só abrir o iniciar e digitar variáveis, ele vai aparecer uma opção chamada: "Editar variáveis de ambiente do sistema".
E lá vai ter a PATH, é só adicionar esse caminho lá, lembrando que tem que mudar o nome do usuário. Feito isso, abra o CMD e digite o comando:
→
C:\Users\alisson adb devices
A saída será:
List of devices attached
Então, conecte o seu celular no computador e, no gerenciamento de conexão USB, marque a opção: "Depuração USB".
Desconecte o USB e conecte de novo, no seu celular irá aparecer uma mensagem para você aceitar a chave do computador. Aceite, e no celular já irá aparecer na lista de devices.
C:\Users\alisson adb devices
List of devices attached
4012e40b device
O meu celular é esse:
4012e40b.
Caso eu queira entrar dentro do dispositivo, é só digitar o seguinte comando:
adb shell
shell@A6020l36:/ $
E aí, já era. Você está dentro do sistema Android.
Pra quem conhece Linux, muitos dos comandos vão funcionar, como o
ls,
cd,
pwd e entre outros comandos básicos.
Pra voltar ao CMD do Windows, é só pressionar
Ctrl+d.
Bom, isso é o básico que é necessário saber por enquanto, pois isso permite saber que o seu celular está corretamente configurado para que sejam efetuados os testes.
Nos testes que foram feitos na empresa, utilizamos basicamente 3 módulos do Python, sendo eles:
- subprocess ( Executa comandos do sistema )
- ElementTree ( Modulo para trabalhar com XML )
- threading ( Modulo de threads para executar testes em paralelo )
No exemplo abaixo, não vou usar threads, mas caso vocês precisem, eu tenho um post específico falando de threads neste link:
Threads em Python
O script completo é esse:
import subprocess
import xml.etree.ElementTree as ET
import time
def get_devices():
output = subprocess.Popen('adb devices', shell=True, stdout=subprocess.PIPE).communicate()[0]
output = str(output).split('attached')[1]
output = output.split('device')[0]
output = output.replace("\\n", "").replace("\\r", "")
output = output.replace("\\t", "")
return output
def get_screen(serial):
output = subprocess.Popen('adb -s %s shell uiautomator dump'%serial,shell=True,stdout=subprocess.PIPE).communicate()[0]
print(output)
output = subprocess.Popen('adb -s %s pull /storage/sdcard0/window_dump.xml'%serial,shell=True,stdout=subprocess.PIPE).communicate()[0]
print(output)
def tap_screen(app_name,x,y):
print("Abrindo %s"%app_name)
output = subprocess.Popen("adb shell input tap %s %s"%(x,y),shell=True,stdout=subprocess.PIPE)
time.sleep(5)
print("Voltando a tela anterior")
output = subprocess.Popen("adb shell input keyevent 4",shell=True,stdout=subprocess.PIPE)
def test_app(node,app_name):
if node.attrib.get('text') == app_name:
position = node.attrib.get('bounds').split(']')[0]
position = position.replace("[","").split(",")
print("Testando APP")
tap_screen(app_name, position[0], position[1])
else:
for n in node.findall('node'):
test_app(n,app_name)
def get_apps():
xml = ET.parse('window_dump.xml')
root = xml.getroot()
app = test_app(root,'Play Store')
device = get_devices()
get_screen(device)
get_apps()
Agora, explicando o que isso faz, a função "get_devices" abaixo:
def get_devices():
output = subprocess.Popen('adb devices', shell=True, stdout=subprocess.PIPE).communicate()[0]
output = str(output).split('attached')[1]
output = output.split('device')[0]
output = output.replace("\\n", "").replace("\\r", "")
output = output.replace("\\t", "")
return output
Ela executa um comando dentro do sistema operacional, o comando "adb devices" vai listar todos os seus dispositivos conectados, então, a saída é armazenada dentro da variável output e, na sequência, vão sendo aplicados alguns comandos para remover caracteres especiais, como quebra de linha e Tabs, para que no final venha só o SERIAL do dispositivo conectado.
Uma vez que tenho esse serial, é possível pegar um dump da tela para saber a posição dos ícones dentro dela.
A tela que eu fiz um dump foi a seguinte:
O script criado vai procurar o botão da "play store", clicar nele e depois voltar para essa mesma tela.
Para que isso aconteça, foi criada a função "get_screen":
def get_screen(serial):
output = subprocess.Popen('adb -s %s shell uiautomator dump'%serial,shell=True,stdout=subprocess.PIPE).communicate()[0]
print(output)
output = subprocess.Popen('adb -s %s pull /storage/sdcard0/window_dump.xml'%serial,shell=True,stdout=subprocess.PIPE).communicate()[0]
print(output)
Essa função roda o comando "adb shell uiatomator dump", que gera um XML com as posições do ícones na tela, uma vez que esse XML foi gerado, ele é copiado para o diretório onde está sendo executado o script, com o comando "adb pull".
Logo na sequência, é executada a função "get_apps()":
def get_apps():
xml = ET.parse('window_dump.xml')
root = xml.getroot()
app = test_app(root,'Play Store')
Essa função, basicamente, lê o arquivo XML gerado pelo "adb", faz o parse dele de string para XML e manda para a função "test_app":
def test_app(node,app_name):
if node.attrib.get('text') == app_name:
position = node.attrib.get('bounds').split(']')[0]
position = position.replace("[","").split(",")
print("Testando APP")
tap_screen(app_name, position[0], position[1])
else:
for n in node.findall('node'):
test_app(n,app_name)
Essa função verifica se o elemento atual do XML possui um atributo chamado "text", e se esse atributo é igual ao nome do APP procurado, que no caso é Play Store. Caso esse APP não seja encontrado, é realizado um "for", buscando os elementos filhos do elemento atual, fazendo assim uma recursividade, até que sejam percorridos todos os elementos filhos desse XML.
Quando o APP é encontrado, é pegado o valor do atributo "bounds" que guarda a posição do item na tela, essa posição é guarda na variável "position" e depois é chamada a função "tap_screen" que clica no botão.
def tap_screen(app_name,x,y):
print("Abrindo %s"%app_name)
output = subprocess.Popen("adb shell input tap %s %s"%(x,y),shell=True,stdout=subprocess.PIPE)
time.sleep(5)
print("Voltando a tela anterior")
output = subprocess.Popen("adb shell input keyevent 4",shell=True,stdout=subprocess.PIPE)
Essa função recebe como parâmetros, o nome do aplicativo somente para que seja informado o nome na tela e as posições x e y do botão, essas posições são passadas para o comando "adb shell input tap" que vai clicar no Play Store, como esse APP demora para abrir, é aguardado o tempo de 5 segundos e depois é executado o comando "adb shell input keyvent 4", que é o equivalente ao botão voltar do Android, então é voltado para a tela anterior.
Alguns dos eventos possíveis são:
- 0 → "KEYCODE_UNKNOWN"
- 1 → "KEYCODE_MENU"
- 2 → "KEYCODE_SOFT_RIGHT"
- 3 → "KEYCODE_HOME"
- 4 → "KEYCODE_BACK"
- 5 → "KEYCODE_CALL"
- 6 → "KEYCODE_ENDCALL"
- 7 → "KEYCODE_0"
- 8 → "KEYCODE_1"
- 9 → "KEYCODE_2"
- 10 → "KEYCODE_3"
- 11 → "KEYCODE_4"
- 12 → "KEYCODE_5"
- 13 → "KEYCODE_6"
- 14 → "KEYCODE_7"
- 15 → "KEYCODE_8"
- 16 → "KEYCODE_9"
- 17 → "KEYCODE_STAR"
- 18 → "KEYCODE_POUND"
- 19 → "KEYCODE_DPAD_UP"
- 20 → "KEYCODE_DPAD_DOWN"
- 21 → "KEYCODE_DPAD_LEFT"
- 22 → "KEYCODE_DPAD_RIGHT"
- 23 → "KEYCODE_DPAD_CENTER"
- 24 → "KEYCODE_VOLUME_UP"
- 25 → "KEYCODE_VOLUME_DOWN"
- 26 → "KEYCODE_POWER"
- 27 → "KEYCODE_CAMERA"
- 28 → "KEYCODE_CLEAR"
- 29 → "KEYCODE_A"
- 30 → "KEYCODE_B"
- 31 → "KEYCODE_C"
- 32 → "KEYCODE_D"
- 33 → "KEYCODE_E"
- 34 → "KEYCODE_F"
- 35 → "KEYCODE_G"
Basicamente, é isso aí, qualquer dúvida é só em dar um salve.