Macaca Macaca 脚本调试思路 (原理、源码 - Python)

王华林 · 2017年03月08日 · 最后由 达峰的夏天 回复于 2017年03月28日 · 1549 次阅读

由于脚本编写过程中通常会涉及到脚本调试的过程(完整的用例、单个步骤等),如果每次都需要重新启动 APP,然后执行相关的步骤才能到达自己想调试的地方,这样的过程费时费力。Macaca 官方只提供了 Nodejs 版本的单步调试思路/工具(https://macacajs.com/zh/debugging )。若是采用该思路,Python 的 Shell 也是可以做到的,即初始化 Driver 并手动操作 APP 至需要调试的地方,再在 Python Shell 中输入需要调试的脚本行,最后运行。但是这种方式过于笨拙,因此,有了如下调试思路。

原理如下流程图:
1、启动 Driver 后,通过 BAT(Windows 下)启动 CMD 窗口,根据 CMD 窗口输入的 CMD 来判断是否执行 run.py(里面可以写需要调试的脚本)。需要调试的时候随时可以更改 run.py 中的脚本,然后再再 CMD 中输入 run 即可。

2、run.py 中的大致流程

如上为整体思路,而具体实现(我是在我的项目中使用的,大家可以 clone 下来看一下效果,以便在自己的项目/自己的语言上实现, https://github.com/Hualiner/UI-auto-test-base-macaca
1、启动 Driver,以及接受 CMD 命令的 py

def drive(server_url, run):
    log = Log()
    log.set_logger(run.get_device()['udid'], run.get_path() + '\\' + 'client.log')

    log.i('platformName: %s', run.get_device()['platformName'])
    log.i('udid: %s', run.get_device()['udid'])
    log.i('package: %s\n', run.get_device()['package'])

    log.i('macaca server port: %d\n', run.get_port())

    # init driver
    driver = WebDriver(run.get_device(), server_url)
    driver.init()

    # set cls.path, it must be call before operate on any page
    path = ReportPath()
    path.set_path(run.get_path())

    # set cls.driver, it must be call before operate on any page
    base_page = BasePage()
    base_page.set_driver(driver)

    while True:
        cmd = input("Please input run or exit:").lower()
        if cmd == 'run':
            print('Run run.py')
            print(os.system('python run.py %s %s %s %s'
                            % (run.get_port(),
                               run.get_device()['udid'],
                               run.get_path(),
                               driver.session_id
                               )
                            )
                  )
        elif cmd == 'exit':
            print('Good Bye')
            break

    # quit driver
    driver.quit()

def main():
    # read all devices on this PC
    devices = Devices().get_devices()

    # read free ports on this PC
    ports = Ports().get_ports(len(devices))

    if not len(devices):
        print('there is no device connected this PC')
        return

    runs = []
    runs.append(RunCases(devices[0], ports[0]))

    # start macaca server
    macaca_server = MacacaServer(runs)
    macaca_server.start_server()

    drive(macaca_server.server_url(runs[0].get_port()), runs[0])

    macaca_server.kill_macaca_server()

if __name__ == '__main__':
    main()

2、run.py 的实现,其中 Run().run() 中的代码即是我们需要调试的代码,随时可更换

class Run(BasePage):
    def run(self):
        # self.click_element_by_accessibility_id('为爱车投保')

        home = PlatformAppHomePage()
        home.click_insurance()

def init():
    port = int(sys.argv[1])
    udid = sys.argv[2]
    report_path = str(sys.argv[3])
    session = sys.argv[4]

    server_url = {
                'hostname': '127.0.0.1',
                'port': port,
    }

    log = Log()
    log.set_logger(udid, report_path + '\\' + 'client.log')

    driver = WebDriver('', server_url)
    driver.attach(session)

    # set cls.path, it must be call before operate on any page
    path = ReportPath()
    path.set_path(report_path)

    # set cls.driver, it must be call before operate on any page
    base_page = BasePage()
    base_page.set_driver(driver)

if __name__ == '__main__':
    init()

    Run().run()

效果如下(clone 源码后,下载 “金惠家 APP”,运行 App/ForTest 下的 run_cases.bat):
1、启动 Driver

2、run

3、exit

也许我们的项目思路不同,所以大家可能会觉得我的流程图未能完整描述我的代码。因为,我只需要让大家知道一个关键点,即 driver.attach(session_id) 就可以了,有了它其他问题只是与自己项目(UI 自动化的思路)实践情况上的差别而已。

另外,Macaca Server(启动手机 APP 后,即 driver.init() 后)与 app-inspector 不能同时使用,请问@xdf老师是否需要解决该问题,如果能解决的话脚本编写/调试就更加方便了。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 8 条回复 时间 点赞
王华林 回复

那是比纯 python 交互式方便些,写代码快

Michael_Wang 回复

启动 Driver 后(获得 session 后),后面就可以手动操作了。需要调试的脚本在 run.py 里面写好,手动操作 App 至需要调试的地方,然后在 CMD 窗口中输入 run 就可以了。后面还需要调试,那就再编写 run.py 里面的脚本,再手动操作 App 至需要调试的地方,再在 CMD 窗口中输入 run。如此反复……

所以,启动 App 只有一次的

交互式调试的话,不应该是直接手动操作进入调试的目的页面吗,然后单步调试。
通过脚本一步步进去,比启动 app 还耗时间吧。

要是能解决就太好啦,期待😃

codeskyblue 回复

多谢大大提供另一种思路!

刚看了 “如何用 ipython 学 appium(https://testerhome.com/topics/7357 )这篇文章,相比我上面的思路,使用 ipython 有一个好处就是 “通过%run 命令运行脚本成功后,脚本中的变量和函数等可以直接在 ipython 中使用”。

但是,把调试目的聚焦过后(我调试的脚本能够正常使用这个 Driver/seesion 才是最终的目的),就会发现,启动 Driver 只是先期的一个准备工作,而其相关的变量/函数是否公用则无关紧要。

因此,解决完 Driver/session 的问题过后,那么就是如何方便调试脚本的输入的问题,如果按照 “如何用 ipython 学 appium” 文中的在 ipython 中输入脚本行(输入一行调试一行),那么对于多个步骤的连续调试(用例、聚合步骤等)就显得有点麻烦(当然,ipython 是否能继续 “通过%run 命令运行需要调试的脚本 py 文件” 我不清楚,文章中没给出这样的示例,如果能这种调试方式也不错)

而我上面的思路,其第一步(启动 Driver/session)与 “如何用 ipython 学 appium” 无差别的,主要差别在后续,即,我的调试脚本依然在 IDE(PyCharm)中编写(调用已有的步骤、聚合步骤、或者像 “如何用 ipython 学 appium” 一样调试新增操作过程),这样调试脚本的编写与其他正式脚本的编写都是在 IDE 中完成(同一个工程中,调用也很方便)。所以,对比来看,我的调试思路是适合我的 UI 自动化思路的。

最后,再次感谢大大提供思路,让我知道有一个 ipython 的存在。我决定粉你😉

群里以前有发过以前如何用 ipython 学 appium 的文章,可以参考下

进程冲突问题在开发 app-inspector 时候就考虑过了,现在应该是有问题才会这样

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册