Recentemente fiz um projeto para efetuar testes em dispositivos android utilizando a linguagem python. Esses testes eram feitos em um celular da samsung conectados via usb a um computador com windows e 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 mostar 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:
https://developer.android.com/studio/install.html
Após fazer a instalação eu coloquei o caminho do adb dentro da variavel PATH do windows.
C:\Users\alisson\AppData\Local\Android\sdk\platform-tools
Não manjo nada de windows, mas no windows 10 é só abrir o iniciar e digitar variáveis e ele vai aparecer uma opção chamada: Editar variáveis de ambiente do sistema.
E lá vai ter a PATH e é só adicionar esse caminho lá, lembrando que tem que mudar o nome do usuário.
Feito isso abra o cmd do windows e digite o comando:
C:\Users\alisson>adb devices
List of devices attached
A saída será a de cima.
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 e no seu celular irá aparecer uma mensagem para você aceitar a chave do computador.
Aceite e o 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:
C:\Users\alisson>adb shell
shell@A6020l36:/ $
eai 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 é 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 especifico 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 variavel output e na sequencia 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 icones 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 icones na tela, uma vez que esse xml foi gerado ele é copiado para o diretorio onde está sendo executado o script com o comando adb pull.
Logo na sequencia é 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ção 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 ai, qualquer dúvida é só em dar um salve.