# api_test_framework_2022 **Repository Path**: peng96/api_test_framework_2022 ## Basic Information - **Project Name**: api_test_framework_2022 - **Description**: 基于requests+pytest的接口自动化测试框架 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2023-02-19 - **Last Updated**: 2023-02-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 框架简介 本框架测试接口来源于微信公众平台的公众号部分的接口,使用微信公众平台提供的测试号服务来获取access_token。 # 项目框架启动 该项目启动顺序 1. 启动`mock/encode_practice_mock.py` 2. 启动`run.py` 注:`data/wx_api/get_token_data.yml`的appid和secret有可能过期失效,导致获取token失败 # 目录结构 - util:工具方法 - data:数据驱动的数据(yml文件) - hotload:热加载。(在代码执行过程中动态调用python中的方法(反射)或得到动态参数) - debug_talk.py:基于自定义函数可辅助实现任意复杂的业务逻辑测试 - log:日志文件夹 - report:allure报告 - temp:allure报告的临时数据 - testcase:测试用例 - .py:pytest测试用例执行文件 - yml:测试用例信息(存放信息:接口的请求数据、数据驱动基本信息、断言信息等) - config_xxx_env.yaml:全局配置文件 - conftest.py:全局fixture固件 - extract.yaml:全局接口关联存储的变量 - pytest.ini:全局pytest配置文件 - run.py:框架入口文件(执行pytest.main()) # 框架执行流程解读 1. pytest的一个用例方法对应一个接口。 2. 执行接口时,我们通过`requests`库来发送请求。 3. 请求发送时会读取框架目录`testcase/yml`下的yml文件,读取该文件的目的是: 1. 读取请求本身的相关信息,包括请求的method、url、param。 2. 读取用例相关信息,包括是否需要数据驱动、接口关联信息、断言信息等。 3. 注意:读取该文件是统一用`util/parametrize_util`的`read_testcase_yaml()`方法,并与`@pytest.mark.parametrize`配合使用。 ```python class TestWxTagApi: ru = RequestsUtil() @pytest.mark.parametrize("case_info", read_testcase_yaml("/testcase/wx_api/yml/wx_get_token.yml")) def test_get_token(self, case_info): TestWxTagApi.ru.standard_yaml(case_info) ``` `testcase/yml`下的yml文件示例: ```yaml - name: $ddt{name} base_url: ${read_config(base,base_spgl_url)} parameterize: name-appid-secret-grant_type-assert_str: /data/wx_api/get_token_data.yml request: method: get url: /cgi-bin/token params: appid: $ddt{appid} secret: $ddt{secret} grant_type: $ddt{grant_type} files: media: "E:\\shu.jpg" extract: access_token: '"access_token":"(.*?)"' bbb: $..expires_in validate: - equals: { status_code: 200 } - contains: $ddt{assert_str} ``` **注意:** - 此时该yml文件是一个数组。 - `testcase/yml`下的yml文件可以不使用`$ddt{}`模板进行数据驱动,也可以直接在yml文件这个数组中。设置多个元素进行数据驱动,此时在`read_testcase_yaml()`中会直接返回这个原本的yml结构。(上面的示例中,该yml文件数组只有一个元素) 4. 执行`read_testcase_yaml()`—— **数据驱动的介入**: 1. 判断testcase/yml的yml文件的数组有多少个元素: 1. ≥两个元素时,直接返回该yml文件; 2. 等于1个元素并且yml文件存在`parameterize`字段时,则对yml文件的`$ddt{}`字符串模板进行替换,并重新返回带有多个元素的数组。(有`parameterize`字段时,yml文件必定使用了模板字符串`$ddt{}`) 5. `standard_yaml()`读取`read_testcase_yaml()`返回的数组的元素。 1. 检查`testcase/yml`下的yml文件的格式是否规范(是否有name,request,valiedate) 2. 发送请求。(调用框架封装好的`request()`) 1. 若yml文件中的请求本身的相关信息含有方法反射的字符串模板,即需要通过反射来读取全局变量或全局关联变量,则会调用框架封装好的`replace_value()`把字符串模板替换为真正的值,其中这里的反射方法来自`hotload`目录下的模块。(如base_url, 请求参数的access_token) 2. 若yml文件中指定了要上传文件的路径,则需要把读取后的文件流赋值给对应的file字段。 3. 最后真正调用`requests`库来发送请求。 3. 如果该yml文件存在`extract`字段,则说明需要接口关联 # 接口签名(sign)规则 1. 获取到所有的参数包括params和body,把所有的参数的key按照ascII码升序排列,如把 {c:3,a:2,b:1} 改成 {a:2,b:1,c:3} 2. 把参数名和参数的值用=连接成字符串,多个参数之间用&连接。 a=2&b=1&c=3 3. 用申请到的appid和appsecret拼接到字符串的头部 appid=admin&appsecret=123&a=2&b=1&c=3 4. 获得订单号nonce并且拼接到字符串的尾部 appid=admin&appsecret=123&a=2&b=1&c=3&nonce=1234567890 5. 获得时间戳timestamp并且拼接到字符串的尾部 appid=admin&appsecret=123&a=2&b=1&c=3&nonce=1234567890×tamp=23523523 6. 进行32的MD5加密,加密之后再大写。 sign=OEWEFWE23234235SDFSDSDF 7. 然后把sign放到url,请求头(一般用这种),请求体里面发送请求