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

王华林 · March 08, 2017 · Last by 达峰的夏天 replied at March 28, 2017 · 1547 hits

由于脚本编写过程中通常会涉及到脚本调试的过程(完整的用例、单个步骤等),如果每次都需要重新启动 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 条回复 时间 点赞

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

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

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 的存在。我决定粉你😉

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

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

Michael_Wang 回复

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

所以,启动 App 只有一次的

王华林 回复

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

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up