pytest使用

news2024/9/22 17:34:23

主要技术内容

1.pytest设计 接口测试 框架设想

common—公共的东西封装

1.request请求

2.Session

3.断言

4.Log

5.全局变量

6.shell命令

❖ config---配置文件及读取

❖ Log—

❖ payload—请求参数—*.yaml及读取

❖ testcases—conftest.py; testcase1.py…….可以有包,最好按模块就近划分

❖ result/report— 运行数据和报告

❖ 测试 策略—组织 运行all_test.py; p0_test.py; ……

2.框架执行简化流程

1.初始化配置

2.查找测试 用例套件
3.运行套件
4.初始化测试 数据pytest.fixture()

5.执行测试用例

6.读取yaml等测试数据

7.request运行

8.assert验证

9.allure记录报告

10.可与ienkins集成发邮件

3.测试 套件设计 模块化思想

4.测试 用例和套件落地思路

搜索功能—五个接口请求—返回股票stock信息;返回讨 论信息,组合,用户,群组;综合

❖ 搜索是个套件,五个接口分别是测试 用例,鉴于pytest 套件的灵活性,可以设计为 一个文件一个类下-【搜索】 下5个不同测试 方法【接口】(或可一个文件一个方法一 个接口设计 也行,灵活性强。这需要根据实际权 衡,如 果需要灵活多选择 后者,如果相对稳 定建议前者)

❖ 如果是通用的功能,可以放在共享的文件中-conftest.py

5.pytest的相关回顾

1. pytest是一个非常成熟的全功能的Python测试 框架

2. 简单 灵活,容易上手;支持参数化;

3. 能够支持简单 的单元测试 和复杂的功能测试 ,还可以用来做 selenium/appnium等自动化测试 、接口自动化测试 (pytest+requests);

4. pytest具有很多第三方插件,并且可以自定义扩 展, 比较好用的如pytest -selenium(多浏览 器执行)、 pytest-html(完美html测试报 告生成)、 pytest-rerunfailures(失败case重复执行)、 pytest-xdist(多CPU分发 )等;pytest-assume(允许每次测试 多次失败),

5. 测试 用例的skip和xfail处理;

6. 可以很好的和jenkins集成;

7. https://docs.pytest.org/en/latest/contents.html#toc 

pytest安装,导入相关依赖库

Pip install –U pytest U表示升级 ➢ Pip intall –U pytest-html ➢ pip install -U pytest-rerunfailures ➢ Pip install pytest-assume ➢ Pip install pytest-allure ➢ Pip install pytest-xdist ➢ Pip list查看 ➢ Pytest –h 帮助 兹测试学

Pytest框架结构

Import pytest

类似的setup,teardown同样更灵活,

模块级 (setup_module/teardown_module)模块始末,全局的

函数级(setup_function/teardown_function)不在类中的函数有用

类级 (setup_class/teardown_class)只在类中前后运行一次

方法级(setup_method/teardown_methond)运行在类中方法始末 

session() 


#这是一个模块model
import pytest


@pytest.fixture(scope="module", autouse=True)
def setup_model():
    print("setup_model")
    yield
    print("teardown_model")

#类class在模块中定义,它们可以包含多个方法和数据成员。
class TestClassA:

    def setup_class(self):
        print('\nsetup class')


    def teardown_class(self):
        print('\nteardown_class')


    #方法method是与类关联的函数
    def setup_method(self):
        print("\nsetup_method")


    def teardown_method(self):
        print("\nteardown_method")

    def test_method_1(self):
        print("TestClassA: test_method_1")

    def test_method_2(self):
        print("TestClassA: test_method_2")


class TestClassB:
    def test_method_1(self):
        print("TestClassB: test_method_1")

    def test_method_2(self):
        print("TestClassB: test_method_2")


#全局函数function,class内的函数function是方法method


def setup_function():
    print("setup_function")


def teardown_function():
    print("teardown_function")


def test_function_1():
    print("test_function_1")


def test_function_2():
    print("test_function_2")



if __name__ == "__main__":
    pytest.main()

module

import pytest

# 功能函数
def multiply(a, b):
    return a * b

# ====== fixture ======
def setup_module():
    print("setup_module():在整个模块之前执行")

def teardown_module():
    print("teardown_module():在整个模块之后执行")

def setup_function(function):
    print("setup_function():在每个方法之前执行")

def teardown_function(function):
    print("teardown_function():在每个方法之后执行")

def test_multiply_1():
    print("正在执行test1")
    assert multiply(3, 4) == 12

def test_multiply_2():
    print("正在执行test2")
    assert multiply(3, 'a') == 'aaa'


 

class

def multiply(a, b):
    """Return the product of two numbers."""
    return a * b


# Test fixture for the multiply function
class TestMultiply:
    def setup_class(cls):
        print("setup_class(): Executed before any method in this class")

    def teardown_class(cls):
        print("teardown_class(): Executed after all methods in this class")

    def setup_method(self):
        print("setup_method(): Executed before each test method")

    def teardown_method(self):
        print("teardown_method(): Executed after each test method")

    def test_multiply_3(self):
        """Test multiply with two integers."""
        print("Executing test3")
        assert multiply(3, 4) == 12

    def test_multiply_4(self):
        """Test multiply with an integer and a string (should fail)."""
        print("Executing test4")
        # Note: This test will raise a TypeError due to incompatible types.
        assert multiply(3, 'a') == 'aaa'

Pytest框架assert断言使用 断言:支持显示最常见的子表达式的值,包括调用,属性,比较以及二元和一元运算符。 包含,相等,不等,大于 小于运算,assertnot 假

assert “h” in “hello”(判断h在hello中)

assert 3==4(判断3=4)

assert 3!=4(判断3!=4)

assert f() ==4 (判断f()方法返回值是否=4)

assert 5>6 (判断5>6为真)

assert not xx (判断xx不为真)

assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'} 断言列表或者字典

import pytest

def intest():
    assert 'h' in "hello"
    assert 3==4
    # (判断3=4)
    assert 3!=4
    # (判断3!=4)
    assert f() ==4
    # (判断f()方法返回值是否=4)
    assert 5>6
    # (判断5>6为真)
    assert not xx
    # (判断xx不为真)
    assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
    # 断言列表或者字典


def add(a, b):
    return a+b
# 测试相等

def test_add1():
    assert add(3, 4) == 7


# 测试不相等
def test_add_2():
    assert add(17, 22) != 50


# 测试大于或等于
def test_add_3():
    assert add(17, 22) <= 50


# 测试小于或等于
def test_add_4():
    assert add(17, 22) >= 38

# 功能:用于判断素数
def is_prime(n):
    if n <= 1:
        return False
    for i in range(2, n):
        if n % i == 0:
            return False
    return True

# 判断是否为True
def test_true_1():
    assert is_prime(13)

# 判断是否为True
def test_true_2():
    assert is_prime(7) is True
# 判断是否不为True
def test_true_3():
     assert not is_prime(4)
# 判断是否不为True
def test_true_4():
    assert is_prime(6) is not True
# 判断是否为False
def test_false_1():
    assert is_prime(8) is False

# 测试包含
def test_in():
 a = "hello"
 b = "he"
 assert b in a

# 测试不包含
def test_not_in():
    a = "hello"
    b = "hi"
    assert b not in a


if __name__ == "__main__":
    pytest.main()

pytest-fixture的灵活

Fixture优势 :

1、对于setup,teardown,可以不起这两个名字,所以命名方式 灵活。(可独立命名,声明后激活)前置后置

import pytest

@pytest.fixture()
def login():
    print('这是个登陆模块!')


def test_soso(login):
    print('case1:登际后执行搜索')


def test_cakan():
    print('case2:不登陆就看')


def test_cart(login):
    print('case3:登陆,加购物车')

2、数据共享。在conftest.py配置里写方法可以实现 数据共享 ,不需要import导入。可以跨文件共享

创建conftest文件

import pytest

@pytest.fixture()
def login():
    print('From_conftest - 这是个登陆模块!')
import pytest


def test_soso(login):
    print('case1:登际后执行搜索')


def test_cakan():
    print('case2:不登陆就看')


def test_cart(login):
    print('case3:登陆,加购物车')

3、 神奇的yield,提供返回值,不是return;;相当于setup 和 teardown 

import pytest


@pytest.fixture(scope="module", autouse=True)
def setup_model():
    print("setup_model")
    yield
    print("teardown_model")


def test_soso(setup_model):
    print('case1:登际后执行搜索')


def test_cakan():
    print('case2:不登陆就看')


def test_cart(setup_model):
    print('case3:登陆,加购物车')



if __name__ == "__main__":
    pytest.main()

pytest-fixture

pytest-fixture-CSDN博客

1.Fixture(scope=“function”,params=None, autouse=False, ids=None, name=None):

2.可以使用此装饰器定义fixture

1. scope:=module,session,class,function;

function:每个test都运行,默认是function的scope ,

class:每个 class的所有test只运行一次;

module:每个module的所有test只运行一次;

session:每个session只运行一次


#这是一个模块model
import pytest


@pytest.fixture(scope="class", autouse=True)
def setup_model():
    print("setup_model_class")
    yield
    print("teardown_model_class")

#类class在模块中定义,它们可以包含多个方法和数据成员。
class TestClassA:

    def setup_class(self):
        print('\nsetup class')


    def teardown_class(self):
        print('\nteardown_class')


    #方法method是与类关联的函数
    def setup_method(self):
        print("\nsetup_method")


    def teardown_method(self):
        print("\nteardown_method")

    def test_method_1(self):
        print("TestClassA: test_method_1")

    def test_method_2(self):
        print("TestClassA: test_method_2")


class TestClassB:
    def test_method_1(self):
        print("TestClassB: test_method_1")

    def test_method_2(self):
        print("TestClassB: test_method_2")


#全局函数function,class内的函数function是方法method


def setup_function():
    print("setup_function")


def teardown_function():
    print("teardown_function")


def test_function_1():
    print("test_function_1")


def test_function_2():
    print("test_function_2")



if __name__ == "__main__":
    pytest.main()

2. params:可选多个参数调用fixture

3. Autouse:如果是True,则为 所有测试 激活fixture,如果是False,则需要显式参考来激活。


#这是一个模块model
import pytest


@pytest.fixture(scope="class")
def setup_model():
    print("setup_model_class")
    yield
    print("teardown_model_class")

#类class在模块中定义,它们可以包含多个方法和数据成员。
class TestClassA:

    def setup_class(self):
        print('\nsetup class')


    def teardown_class(self):
        print('\nteardown_class')


    #方法method是与类关联的函数
    def setup_method(self):
        print("\nsetup_method")


    def teardown_method(self):
        print("\nteardown_method")

    def test_method_1(self,setup_model):
        print("TestClassA: test_method_1")

    def test_method_2(self):
        print("TestClassA: test_method_2")


class TestClassB:
    def test_method_1(self):
        print("TestClassB: test_method_1")

    def test_method_2(self):
        print("TestClassB: test_method_2")


#全局函数function,class内的函数function是方法method


def setup_function():
    print("setup_function")


def teardown_function():
    print("teardown_function")


def test_function_1():
    print("test_function_1")


def test_function_2():
    print("test_function_2")



if __name__ == "__main__":
    pytest.main()

4. ids:每个字符串ID的列表,每个字符串对应 于param,这是测试 ID的一部分。  

import pytest


@pytest.fixture(params=['www.baidu.com','www.bingying.com'],
                ids=['case1','case2'])
def urls(request):
    return request.param


def test_url(urls):
    url = urls
    print("url:",url)

5. Name:fixture的名称。

3.fixture带参数传递 -通过固定参数request传递 ;传二个以上参数(通过字典,其实算一个),可 以传多个组合测试 。

import pytest


@pytest.fixture(params=['www.baidu.com','www.bingying.com'],
                ids=['case1','case2'])
def urls(request):
    return request.param


def test_url(urls):
    url = urls
    print("url:",url)

传一个字典

import pytest


@pytest.fixture(params=[{'name':'baidu','url':'www.baidu.com'},{'name':'biying','url':'www.biying'}])
def urls_name(request):
    url = request.param['url']
    name = request.param['name']
    return url,name


def test_name_url(urls_name):
    url,name = urls_name
    print(name,url)

4.参数化:@pytest.mark.parametrize(“login_r”, test_user_data, indirect=True) indeirect=True 是把 login_r当作函数去执行

fixture传二个参数与数据驱动结合

@pytest.mark.parametrize 装饰器接受两个主要参数:

  1. 第一个参数是一个字符串,表示测试函数中需要参数化的参数名称,多个参数之间用逗号分隔。
  2. 第二个参数是一个列表或元组,其中每个元素都是一个参数组合,每个组合也是一个元组或列表,其元素顺序必须与第一个参数中列出的参数顺序相匹配。

直接在测试函数上使用

import pytest

# 假设我们有一个函数,它接受两个参数并返回它们的和
def add(a, b):
    return a + b

# 使用 parametrize 装饰器,指定测试函数的参数和对应的值
@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),   # 第一组参数
    (2, 3, 5),   # 第二组参数
    (-1, 1, 0),  # 第三组参数
    (0, 0, 0),   # 第四组参数
])
def test_add(a, b, expected):
    assert add(a, b) == expected

类级别使用它,对整个类的所有测试方法进行参数化

两组数据组合

两组数据参数化,从下往上执行

pytest-fixture

1.Fixture 显示调用

2.Fixture 应用usefixtures

3.autouse=True自动激活

4.可以使用多个fixture

5.可以嵌套fixture


 

55:00

fixture结合parametrize+yaml进行搜索接口 测试

读取url

读取yaml文件,请求access_token,在test里create_tag

import pytest
import requests
import os
import yaml


def _base_data(file_name):
    cur_path = os.path.dirname(os.path.realpath(__file__))
    yaml1 = os.path.join(cur_path, file_name)
    f1 = open(yaml1)  # 打开yaml文件
    data = yaml.load(f1, Loader=yaml.FullLoader)  # 使用load方法加载
    return data

@pytest.fixture(autouse=True)
def get_base_data():
    base_data = _base_data('tag.yml')
    for v in base_data.values():  #读值
        print(v)
        return v


test_user_data2 = [{"tag": {"name": 'mou1moumou'}},{"tag": {"name": 'maimai2'}},{"tag": {"name": 'sl2ience'}}]


@pytest.fixture(autouse=True)
def query_param(request):
    return request.param


@pytest.mark.parametrize("query_param", test_user_data2, indirect=True)
def test_tag(get_base_data, query_param):
    method = get_base_data.get('method')
    url = get_base_data.get('url')
    params = get_base_data.get('param')
    res = requests.request(method=method, url=url, params=params)
    data = res.json()
    access_token = data['access_token']
    tag_params = query_param
    response = requests.post('https://api.weixin.qq.com/cgi-bin/tags/create?access_token='+access_token, json=tag_params)
    assert 'tag' in response.json()

读取测试数据

读取多个测试数据,待解决!
import yaml
import os
import pytest

def _base_data(file_name):
    cur_path = os.path.dirname(os.path.realpath(__file__))
    yaml1 = os.path.join(cur_path, file_name)
    f1 = open(yaml1)  # 打开yaml文件
    data = yaml.load(f1, Loader=yaml.FullLoader)  # 使用load方法加载
    return data


@pytest.fixture(scope="module")
def get_test_data():
    test_data = _base_data('tag_data.yml')
    test_user_data2 = test_data.get('test_user_data2')
    return test_user_data2


@pytest.mark.parametrize("tag_data", get_test_data(), indirect=True)
def test_functionality(tag_data):
    # test_case 是一个字典,包含 'name', 'input', 'expected_output' 等键
    response= requests.get(url='https://api.weixin.qq.com/cgi-bin/token', param={'grant_type': 'client_credential',
                               'appid': 'wx2e193b26f5edc5cf',
                               'secret': '99fe9aa25e42f30fa81c5d83816e4fc7'})
    access_token = response['access_token']
    response_tag = requests.post('https://api.weixin.qq.com/cgi-bin/tags/create?access_token=' + access_token, json= tag_data)
    print(response_tag)

附:fixture工厂模式

私有方法

html测试报告

安装 

pip install pytest-html

 执行

pytest --html=report.html -vs test_yml_func.py

在当前目录生成report.html

Allure 见其他文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1944811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Chapter 14 Python数据容器总结

欢迎大家订阅【Python从入门到精通】专栏&#xff0c;一起探索Python的无限可能&#xff01; 文章目录 前言一、数据容器对比二、数据容器的通用操作【拓展】字符串大小比较 前言 Python 作为一种高级编程语言&#xff0c;内置了多种强大的数据容器&#xff0c;帮助开发者以更…

【GoLang】Golang 快速入门(第一篇)

目录 1.简介&#xff1a; 2.设计初衷&#xff1a; 3.Go语言的 特点 4.应用领域: 5.用go语言的公司&#xff1a; 6. 开发工具介绍以及环境搭建 1.工具介绍: 2.VSCode的安装: 3.安装过程&#xff1a; 4.Windows下搭建Go开发环境--安装和配置SDK 1.搭建Go开发环境 - 安装…

【洛谷】P1088 [NOIP2004 普及组] 火星人——C++

本题我们会用到函数next_permutation(start,end),是头文件algorithm标准库中的一个标准函数&#xff0c;用来表示[start,end]内存的数组中产生一个字典排序&#xff0c;比如[1 , 2 ,3]到[2 ,3, 1]再到[3 , 1, 2]这样的&#xff0c;这个函数的复杂度为&#xff08;n!&#xff09…

Rust代码答疑报错|Python一对一辅导答疑

Question 你好&#xff0c;我是悦创。 学员答疑&#xff1a; https://code.bornforthis.cn/?id4e72084d-1eaf-44ed-8067-744671491574https://code.bornforthis.cn/?id664ff169-41d6-409f-a05b-02ed42279759 问题代码&#xff1a; // You can bring module paths into sc…

开发日志:windows修复SSL漏洞CVE-2016-2183(3389端口)

漏洞危害&#xff1a; 具有足够资源的中间人攻击者可利用此漏洞,通过“birthday”攻击检测会在固定密码与已知纯文本之间泄露 XOR 的冲突,进而泄露密码文本(例如安全 HTTPS Cookie),并可能导致劫持经认证的会话。 参见《支持SSL 64位块大小的密码套件(SWEET32)-修复方案》 参考…

html 常用css样式及排布问题

1.常用样式 <style>.cy{width: 20%;height: 50px;font-size: 30px;border: #20c997 solid 3px;float: left;color: #00cc00;font-family: 黑体;font-weight: bold;padding: 10px;margin: 10px;}</style> ①宽度&#xff08;长&#xff09; ②高度&#xff08;宽&a…

pikachu 之CSRF(跨站请求伪造)get和post型

CSRF&#xff08;跨站请求伪造&#xff09; 概念 跨站请求伪造&#xff08;Cross-Site Request Forgery&#xff0c;简称CSRF&#xff09;是一种攻击方式&#xff0c;攻击者通过伪造用户的请求&#xff0c;欺骗受害者在不知情的情况下执行不想要的操作。这种攻击利用了用户已经…

使用python连接neo4j时报错:IndexError: pop from an empty deque的解决办法

遇见这个错&#xff0c;首先可能是python现在的py2neo的版本不对&#xff0c;把2021.1.0版本卸载&#xff0c;下载 py2neo4.2.0版本。我不是&#xff0c;一阵搜&#xff0c;发现需要改配置文件 首先找到你的neo4j的安装路径 在网上看的是&#xff0c;先找到data/dbms/auth文件…

产品升级|宏基因组产品增加新成员:多酚代谢注释数据库

多酚&#xff08;polyphenol&#xff09;是一种化学多样且丰富的植物衍生化合物&#xff0c;包括10000多个化学式和几个结构家族&#xff0c;包括聚合物&#xff08;例如单宁&#xff09;、单体&#xff08;例如类黄酮&#xff09;和简单酚类&#xff08;例如酚酸&#xff09;。…

nginx漏洞修复 ngx_http_mp4_module漏洞(CVE-2022-41742)【低可信】 nginx版本升级

风险描述&#xff1a; Nginx 是一款轻量级的Web服务器、反向代理服务器。 Nginx 的受影响版本中的ngx _http_mp4_module模块存在内存越界写入漏洞&#xff0c;当在配置中使用 mp4 directive时&#xff0c;攻击者可利用此漏洞使用使用ngx_http_mp4_module模块处理特制的音频或视…

服务攻防-框架安全(漏洞复现)

关闭靶场 sudo docker-compose down 运行此靶场 sudo docker-compose up -d 查看启动环境 sudo docker ps 运行dockers容器 docker exec -it 64052abd288b /bin/bash thinkphp框架 thinkphp 2 - rce漏洞复现 docker exec -it 731dbae0e0b5 /bin/bash 集成化工具扫描 可以命令…

【豆包Marscode体验官】揭秘MarsCode AI编辑助手:高效智能编辑新纪元之入门指导与最佳实践

文章目录 1. 概述2. 工具使用过程2.1 MarsCode插件简介2.2 安装和配置2.2.1 安装MarsCode插件2.2.2 配置MarsCode插件 2.3 各个功能的使用2.3.1 代码补全2.3.2 代码补全 Pro【操作提示&#xff0c;No suggestion from Model&#xff0c;不知道是不是版本的问题】2.3.3 代码生成…

爬虫学习2:爬虫爬取网页的信息与图片的方法

爬虫爬取网页的信息与图片的方法 爬取人物信息 import requestshead {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0" } # 这是get请求带参数的模式…

boost::asio网络编程

目录 基础操作端点的生成创建socket服务端创建acceptor用于监听传入的连接请求 并 接受连接服务器绑定端口客户端通过ip连接服务端 connect客户端通过域名连接服务端服务器监听 接受连接读写buffer同步的读写实现一个同步读写的服务器与客户端应答 异步读写函数异步写异步读官方…

冒泡排序(数组作为函数参数)

什么是冒泡排序&#xff1f; 冒泡排序&#xff08;Bubble Sort&#xff09;也是一种简单直观的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换&#xff0c;…

CVE-2020-7248 OpenWRT libubox标记二进制数据序列化漏洞(更新中)

提要 该文档会一直处于更新当中&#xff0c;当状态为完毕后&#xff0c;才是更新完成。由于网络上关于该漏洞原理的分析文档和资源实在是太少&#xff0c;而本人关于该方向也才是刚入门&#xff0c;能力有限&#xff0c;所以复现需要的时间较长&#xff0c;需要补充和学习的东西…

中国的AI技术水平,是否已经到了能够帮助创作者实现内容的程度?

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量https://aitools.jurilu.com/ 是的&#xff0c;早就实现了&#xff0c;我从上年开始用AI辅助写小说&#xff0c;已经挣了2万多。 这是我在番茄那开的小号&#xff0…

Redis的集群模式

1. Redis三种集群模式 Redis 提供的三种集群模式各有其特点和适用场景&#xff0c;以下是对这三种模式的简要概述&#xff1a; 主从模式&#xff08;Master-Slave Replication&#xff09;&#xff1a; 在这种模式下&#xff0c;数据在主节点&#xff08;Master&#xff09;上…

服务器部署环境(docker安装Mysql + Redis + MongoDB)

1. 安装Docker 1、选择要安装的平台 Docker要求CentOS系统的内核版本高于3.10 uname -r #通过 uname -r 命令查看你当前的内核版本官网地址 2. 卸载已安装的Docker, 使用Root权限登录 Centos。确保yum包更新到最新。 sudo yum update如果操作系统没有安装过Docker , 就不需要…

自动化测试之python操作Oracle遇到的问题:Oracle-Error-Message: DPI-1047

自动化测试需要连接数据库&#xff0c;进行一些增删改查的操作&#xff0c;那么针对python语言&#xff0c;如何才能够通过程序完成连接数据库并且进行操作呢&#xff1f;可以通过cx_oracle这个模块来实现。 一、问题描述&#xff1a; 下面是我在实践的过程中遇到的问题&#…