Implementation of deep UI traversal tool code implementation based on Appium

Series of articles:

Implementing a deep UI traversal tool based on Appium

Implementing a deep UI traversal tool based on Appium (2)

Implementing a deep UI traversal tool based on Appium (3)

I finally came to the place where the code was written. In advance, all the code will be put on github as my open source project and will be maintained in the future. I will post the open source address at the end of the article.

Let's create the project appium_uicrawler, and then create the directory mentioned in the previous article,

In the configuration file, we configure some commonly used data. run.py is used as the main entry of the project. The apk to be tested is placed in installapk, and the installation and package name are all obtained through this.

The above is the approximate directory structure. Next, it is to write some commonly used configuration files as some configurations for running traversal tests, which are written in yaml files to facilitate maintenance. This configuration file is different from config. config is used for some common data in the code, and the yaml file is the traversal strategy. Later, we initialize the UI traversal configuration execution according to the yaml file configuration.

The configured yaml is named config.yaml, placed in the file path, and executed to obtain the configuration file. A flexible path can be used in the later stage. A bottom-up strategy is configured here. If the latest one cannot be loaded, we will execute it according to this bottom-line strategy. Can be executed in the default configuration, the initialization strategy is as follows

LOGINELEMENT:
  #Turn on automatic login
  AUTOLOGIN: false

  #Android login related elements and operations
  LOGIN_ELEMENTS_ANDROID:
    - ANDROID_USERNAME:
        XPATH: '//*[@resource-id="login"]'
        ACTION: input
        VALUE: '13691034101'
    - ANDROID_PASSWORD:
        XPATH: '//*[@resource-id="password"]'
        ACTION: input
        VALUE: '123456'
    - ANDROID_LOGIN_BUTTON:
        XPATH: '//*[@resource-id="test_login_button"]'
        ACTION: click
        VALUE: '1'
#Global configuration
GENERAL:

  #hit count
  MAX_CLICK_COUNT: 10000
  #time to find an element
  DEFAULT_WAIT: 0.2
  #Find the interval between elements
  INTERVAL_SEC: 0.1
  #Whether to ignore Crash, when set to true,
  IGNORE_CRASH: true
  #Running time limit (minutes)
  RUNNING_TIME: 100
  #Number of swipes Whether the page is swiped without change,
  SCPRE_AUTO: true
  #Number of swipes Number of interactions without page change
  SCPRE_NUM: 2
  #Controls whether to generate screenshots. When false, no screenshots will be generated, but it can improve the running speed.
  SCREEN_SHOT: true
  #Control whether to generate video
  VIDEO: true
  #The screenshot shows the number of steps when crashing
  CRASH_PIC_COUNT: 10
  #Traversal depth
  MAX_DEPTH: 800
LIST:
  #tarbar
  ANDROID_BOTTOM_TAB_BAR_ID: '@resource-id="com.qihoo.browser:id/title"'
  #Type of input text
  INPUT_CLASS_LIST:
    - android.widget.EditText

  #The text to be entered is in a 1:1 ratio
  INPUT_TEXT_LIST:
    - 'leizi'
    - '123'

  #When the following text appears in the UI element, trigger the back key (iOS triggers the return operation by swiping from left to right)
  PRESS_BACK_TEXT_LIST:
    - return
    - Privacy Agreement

  #When pp jumps to the following app, trigger the back key
  PRESS_BACK_PACKAGE_LIST:
    - com.android.settings

  #Trigger the back key when the following Activity is currently encountered
  PRESS_BACK_ACTIVITY_LIST:
    - com.autohome.mainlib.business.ui.commonbrowser.activity.CommBrowserActivity

  #Controls that contain the following text are not clicked
  ITEM_BLACKLIST:
    - customer service
    - quit
    - Telephone
    - Refuse
    - Photograph
    - prohibit
    - call
    - low power mode

  #In addition to the package name of the APP itself, it is judged whether to jump out of the APP according to the following package names. When the app jumps to the following app, it is considered legal, and the traversal operation will continue to be implemented.
  ANDROID_VALID_PACKAGE_LIST:


  #Whitelist, when encountering a control containing the following text, it will be clicked multiple times (by default, all controls are only clicked once), which is used in UI traversal
  ITEM_WHITE_LIST:
    - Sure
    - allow
    - Cancel
    - submit
    - Cancel

  #Do not click the following types of controls and their child elements
  IOS_EXCLUDE_BAR:
    #keyboard
    - XCUIElementTypeKeyboard

  #Do not click on elements of the following types
  IOS_EXCLUDE_TYPE:
    - XCUIElementTypeApplication

  #Do not click on elements of the following types
  ANDROID_EXCLUDE_TYPE:
    - android.widget.ImageButton
copy

After initializing the Ui traversal execution time, whether to log in automatically, etc., we will write a file to read the configuration.

import yaml


class Parse(object):
    def __init__(self, filepath):
        self.filepath = filepath
        self.reslut = {}
        self.reslut = self.__init()

    def __init(self):
        file = open(self.filepath, 'r', encoding="utf-8")
        file_data = file.read()
        file.close()
        self.reslut = yaml.load(file_data)
        return self.reslut

    def getlogin(self):
        return self.reslut['LOGINELEMENT']

    def getgeneral(self):
        return self.reslut['GENERAL']

    def getlist(self):
        return self.reslut['LIST']

    def get_tar(self):
        return self.getlist()['ANDROID_BOTTOM_TAB_BAR_ID']

    def get_text_input(self):
        return self.getlist()['INPUT_CLASS_LIST']

    def get_sendText(self):
        return self.getlist()['INPUT_TEXT_LIST']

    def get_back(self):
        return self.getlist()['PRESS_BACK_TEXT_LIST']
    def get_back_packname(self):
        return self.getlist()['PRESS_BACK_PACKAGE_LIST']

    def get_activity(self):
        return self.getlist()['PRESS_BACK_ACTIVITY_LIST']

    def get_block(self):
        return self.getlist()['ITEM_BLACKLIST']

    def get_white(self):
        return self.getlist()['ITEM_WHITE_LIST']

    def get_not_click(self):
        return self.getlist()['ANDROID_EXCLUDE_TYPE']

    def get_ios_bar(self):
        return self.getlist()['IOS_EXCLUDE_BAR']

    def get_run_time(self):
        return self.getgeneral()['RUNNING_TIME']

    def get_ignor_crash(self):
        return self.getgeneral()['IGNORE_CRASH']

    def get_find_element_wait(self):
        return self.getgeneral()['INTERVAL_SEC']

    def get_find_element_timeout(self):
        return self.getgeneral()['DEFAULT_WAIT']

    def get_click_ccont(self):
        return self.getgeneral()['MAX_CLICK_COUNT']

    def get_max_deep(self):
        return self.getgeneral()['MAX_DEPTH']

    def crashPic(self):
        return self.getgeneral()['CRASH_PIC_COUNT']

    def screen(self):
        return self.getgeneral()['SCREENSHOT_COUNT']

    def vido(self):
        return self.getgeneral()['VIDEO']

    def issceern(self):
        return self.getgeneral()['SCREEN_SHOT']
    def opearlogin(self):
        all_login=[]
        reslut=self.getlogin()['LOGIN_ELEMENTS_ANDROID']
        for item in reslut:
            for key,value in item.items():
                all_login.append(value)
        return all_login

    def getmonkeyConfig(self):
        return self.reslut['MONKEYCONFIG']

    def auto_loggin(self):
        return  self.getlogin()['AUTOLOGIN']

    def  scoreauto(self):
        return  self.getgeneral()['SCPRE_AUTO']

    def autoscorecount(self):
        return  self.getgeneral()['SCPRE_NUM']
copy

After the writing is completed, we will encapsulate a preliminary script to read the configuration. The above is the reading of the script. There is no logic. It is to read the file normally, and then read the configuration of yaml. yaml file reading is also very simple, and after reading, it is a dict, just parse the corresponding dict.

In the process of running, it is also necessary to obtain the test log of the execution process. We encapsulate a method for obtaining the log during the execution process. Create execlog.py

import os
def run_adb_log(dev, path):
    '''
    implement adb Obtain log
    '''
    cmd = 'adb -s {} shell logcat -c'.format(dev)
    os.system(cmd)
    filepath = os.path.join(path, dev)
    cmdlog = 'adb -s {} logcat -v threadtime >{}.log'.format(dev, filepath)
    os.system(cmdlog)
copy

Here, just use python to execute the adb command. When starting, if you want to get the log, you can use a separate thread/process to handle this.

For the log of the running process, that is, to record the log of our code, create log.py

import os
import logbook
from logbook.more import ColorizedStderrHandler
from functools import wraps
from common.Makecasenum import call_num
check_path = '.'
LOG_DIR = os.path.join(check_path, 'testlog')
if not os.path.exists(LOG_DIR):
    os.makedirs(LOG_DIR)
def get_logger(name='UI_bianli', level=''):
    """ get logger Factory function """
    logbook.set_datetime_format('local')
    ColorizedStderrHandler(bubble=False, level=level).push_thread()
    filename = name + call_num
    logbook.FileHandler(
        os.path.join(LOG_DIR, '%s.log' % filename), bubble=True, encoding='utf-8').push_thread()
    return logbook.Logger(name)
LOG = get_logger(level='INFO')
copy

Makecasenum used here

import datetime
import hashlib


def make_md5(make_user):
    md5make = hashlib.new('md5', make_user.encode('utf-8')).hexdigest()
    return md5make
call_num = make_md5(datetime.datetime.now().strftime('%Y-%m-%d-%H:%M'))
copy

An md5 formed according to the timestamp is used as an identifier.

This time, I shared the configuration of the general strategy, the module to read, the module to run the log, and the module to record the mobile phone log. The next article will share the code of webdriver encapsulation and so on.

In addition, the appium environment construction can refer to

This article takes you through the pits encountered in building the appium test environment on mac

All code addresses:

https://github.com/liwanlei/appium_uicrawler
copy

Find problems and solve them. If you encounter problems, solve them slowly.

Posted by TobesC on Sat, 21 May 2022 07:12:43 +0300