小程序自动化测试之 Code 换取 Token

522 阅读3分钟

需求梳理:

微信小程序的登录流程

developers.weixin.qq.com/miniprogram…

小程序登陆需要在小程序内部调用wx.login()的方法拿到code,然后去请求服务获取到token后续的接口才能请求。

Python 使用minium,JS 使用miniprogram-automator,这是微信官方提供的 UI 自动化测试的工具,它们的原理都是调起微信开发者工具,然后会对外提供了一个自动化测试的端口,然后它们的 API 应该都是和这个端口进行通信。

1、miniprogram-automator 使用文档: developers.weixin.qq.com/miniprogram…

2、minium MiniTest 使用文档: minitest.weixin.qq.com/#/minium/Py…

运行环境

  • Python 3.8及以上
  • 微信开发者工具 (本文档中简称IDE)最新版本,并打开安全模式: 设置 -> 安全设置 -> 服务端口: 打开
  • 微信 >= 7.0.7 (确认微信公共库版本 >= 2.7.3即可)

安装

自动安装pip3 install minium或者

pip3 install [https://minitest.weixin.qq.com/minium/Python/dist/minium-latest.zip](https://minitest.weixin.qq.com/minium/Python/dist/minium-latest.zip)

如果需要ios真机测试, 会额外安装一些依赖库

pip3 install minium[ios]

手动安装 下载minium安装包, 解压后进入文件夹, 运行

python3 setup.py install

环境检查

minium安装完成后,可执行以下命令查看版本:

minitest -v

出现如以下内容的构建信息则已安装成功

{‘version’: ‘1.1.0’, ‘revision’: ‘2fac2e36c281213874110f2088bc08c570bc1a2d’, ‘branch’: ‘master’, ‘update_at’: ‘2021-08-05 21:17:09’}

开发者工具自动化能力检查

“path/to/cli” auto --project “path/to/project” --auto-port 9420

例:

C:\Program Files (x86)\Tencent\微信web开发者工具> .\cli.bat auto --project “D:\test-data\home-elev-master” --auto-port 9420

脚本开发

1、原理:Python 使用minium,使用minium.app.call_wx_method()方法调用wx.login()方法就可以获取code

开发文档:minitest.weixin.qq.com/#/minium/Py…

2、编写调试脚本

#!/usr/bin/env python3
import minium
mini = minium.Minium({
            "project_path": "D:\\test-data\home-elev-master",   # 小程序的路径
            "dev_tool_path": "C:\Program Files (x86)\Tencent\微信web开发者工具\cli.bat"  # 开发者工具的路径
        })
# 调用wx.login()方法获取code        
login_info = mini.app.call_wx_method("login")
wx_code = login_info["result"]["result"]["code"]
print(wx_code)
# 调试前注意环境的设置

3、封装

#!/usr/bin/env python3
import minium
class WxLogin:
    def __init__(self):
        self.mini = minium.Minium({
            "project_path": "D:\\test-data\home-elev-master",   # 小程序的路径
            "dev_tool_path": "C:\Program Files (x86)\Tencent\微信web开发者工具\cli.bat"  # 开发者工具的路径
        })

    def get_login_code(self):
        # 调用wx.login接口获取登录信息
        login_info = self.mini.app.call_wx_method("login")
        # 获取code
        wx_code = login_info["result"]["result"]["code"]
        print(wx_code)
        return wx_code

4、调用

def minigram_login():
    """
    获取token
    """
    url = "xxx"
    headers = {"content-type": "application/json"}
    # 获取code
    wx_code = WxLogin().get_login_code()
    data = {"code": wx_code}
    response = requests.post(url=url, json=data, headers=headers)
    assert response.json()["success"] == 1
    token = response.json()["obj"]["token"]
    print(token)
    return token

Mac端实测

根据手机号授权获取登录信息

import requests
from minium import Minium
import threading

mini = Minium({
    "project_path": "/Users/lizhixiang/Downloads/test-py-auto-test",   # 小程序的路径
    "dev_tool_path": "/Applications/wechatwebdevtools.app/Contents/MacOS/cli"  # 开发者工具的路径
})
# self.native = native

class HookNative(object):
    def __init__(self, native, test) -> None:
        self.native = native
        self.test = test

    def release(self):
        self.test = None
        self.native = None

    def __getattr__(self, name):
        item = getattr(self.native, name)
        if name not in ("screen_shot", "kvtest_get", "capture") and callable(item):

            def wrapper(*args, **kwargs):
                time.sleep(2)
                self.test.capture("before %s" % name)
                ret = item(*args, **kwargs)
                self.test.logger.info("call %s ret %s" % (name, ret))
                self.test.capture("after %s" % name)
                return ret

            return wrapper
        return item

class WxLogin:
    # def __init__(self, native):
    def get_login_code():
        login_info = mini.app.call_wx_method("login")
        wx_code = login_info["result"]["result"]["code"]
        print(wx_code)
        return wx_code

    @classmethod
    def get_user_phone(cls):
        called = threading.Semaphore(0)
        detail = errMsg = None

        def callback(args):
            nonlocal detail, errMsg
            called.release()
            detail = args[0]["detail"]
            errMsg = detail["errMsg"]


        mini.app.hook_current_page_method("testGetPhoneNumber", callback)
        mini.app.get_current_page().get_element("#testGetPhoneNumber").tap()
        # if cls.native.handle_modal("取消", title="微信帐号还没有绑定手机号"):
        #     assert called.acquire(timeout=10), "callback called"
        #     assert "getPhoneNumber:fail" in errMsg
        # else:
        # cls.native.get_user_phone()
        assert called.acquire(timeout=10), "callback called"
        # assert {"errMsg": "getPhoneNumber:ok"} in detail
        return detail

    @classmethod
    def minigram_login(cls):
        url = "接口地址"
        headers = {"content-type": "application/json"}
        wx_code = cls.get_login_code()
        data = {"code": wx_code, "appId": '替换成appId'}
        with requests.Session() as session:
            response = session.post(url=url, json=data, headers=headers)
            response_json = response.json()
            print(response_json)
            openid = response_json["data"]["openId"]
            detail = cls.get_user_phone()
            print(detail)

            query = {"openId": openid, "appId": '替换成appId', "phoneEncryptedData": detail['encryptedData'], "phoneIv": detail['iv']}            onekey_url = '接口地址'
            print(query)
            response_token_res = session.post(url=onekey_url, json=query, headers=headers)
            response_json_token = response_token_res.json()
            print(response_json_token)
            token = response_json_token["data"]["token"]
            print(token)
            return token

if __name__ == '__main__':
    WxLogin.minigram_login()

前端小程序可以使用下面代码测试,备miniprogram-demo-test

附录:

1、小程序自动化框架 minium 官方文档、minium 测试 demo

git.weixin.qq.com/groups/mini…

2、录制回放

developers.weixin.qq.com/miniprogram…

3、微信小程序自动化框架 minium 实践:

developers.weixin.qq.com/community/d…

4、miniprogram-demo-test

git.weixin.qq.com/minitest/mi…