Pytest自动化框架

news2025/4/21 19:32:03

目录

Pytest简单介绍

第一章:Pytest console命令 

1.pytest

-v 参数

-h 参数

其他一些参数:仅供参考

第二章. mark标记 

@pytest.mark.skip

@pytest.mark.skipif

@pytest.mark.xfail 

​编辑 @pytest.mark.patametrize

单个参数

多个参数 

直接标记 

模糊匹配标记 

 使用mark自定义标记

 第三章 固件Fixture

fixture的使用

​编辑fixture参数 

fixture的作用域 

@pytest.mark.usefixtures()

autouse(自动使用)

yeild的使用

注意点:

​编辑

 共享Fixture功能

参数化

内置Fixture

总结


Pytest简单介绍

下载pytest

pip install pytest

第一章:Pytest console命令 

默认需要test开头的py模块,test_开头的方法

1.pytest

执行pytest命令会自动匹配到test开头或者结尾的文件

将其作为测试用例文件执行,在测试用例文件中自动匹配到test开头的类,类中匹配到test开头的方法,然后执行

pass代表断言成功

失败:

-v 参数

会显示是哪个测试用例,信息更加详细一点

-h 参数

help 这是代表帮助的参数

其他一些参数:仅供参考

第二章. mark标记 

pytest.mark是用来对测试方法进行标记的一个装饰器,主要作用是在测试用例执行过程中跳过标记的测试用例或做出判断以选择性的执行

标记测试函数

使用pytest --markers查看官方提供的mark

各个mark的具体含义如下: 

mark含义
@pytest.mark.filterwarings(warning)在标记的测试方法上添加警告过滤    
@pytest.mark.skip(reason=None)执行时跳过标记的测试方法,reason默认为空
@pytest.mark.skipif(condition)通过条件判断是否跳过标记的测试方法,如果condition为真跳过,否则不跳过

@pytest.mark.xfail(condition,reason=None,run=True,

raises=None,strict=False)

如果条件的condition为True,则将预期结果标记为False
@pytest.mark.parametrize(argnames,argvalues)测试函数参数化,即调用多次测试函数,依次传递不同的参数
@pytest.mark.usefixtures(argnames,argvalues)将测试用例标记为需要指定的所有fixture,和直接使用fixture的效果一样。
@pytest.mark.tryfirst标记一个挂钩实现函数,使得所标记的测试方法可以首先或尽早地执行
@pytest.mark.trylast标记一个挂钩实现函数,使得标记的测试方法可以最后或尽可能晚执行,和tryfrist相反

@pytest.mark.skip

创建一个test_skip.py 添加这两个测试方法

使用pytest  -v执行

import pytest


# 如果在测试方法前添加了@pytest.mark.skip(reason=None),则在执行过程中遇到该测试方法
# 时,跳过不执行
@pytest.mark.skip
def test_skip1():
    assert 1 == 2


@pytest.mark.skip(reason="跳过该条测试用例")
def test_skip2():
    assert 3 == 4

运行结果:

@pytest.mark.skipif

condition为True,跳过测试

condition为False, 不跳过

import pytest
import sys

my_list = [1, 3, 4, 5]

"""
源码解析:
    class _SkipifMarkDecorator(MarkDecorator):
        def __call__(  # type: ignore[override]
            self,
            condition: str | bool = ...,
            *conditions: str | bool,
            reason: str = ...,
        ) -> MarkDecorator: ...

condition 可以是字符串也可以是bool类型

"""


# skipif对一些事物进行判断从而决定是否跳过测试方法,只有在满足条件下才会跳过
# condition = True, 跳过测试
@pytest.mark.skipif(condition=lambda: 1 in my_list, reason="1 在 my_list 中,跳过测试")
def test_skipif1():
    assert 1 == 1


# condition为False, 所以不跳过
@pytest.mark.skipif('sys.platform != "win32" ')
def test_skipif2():
    assert 2 == 2

# if __name__ == '__main__':
# print(sys.platform)  # win32

@pytest.mark.xfail 

xfail可以拆分成x和fail理解,x表示可以预期到的结果,fail为失败,合起来可以表达成可以预期到的失败的测试。xfail装饰器解决的是对于很清楚的知道他的记过是失败的,但是又不想直接跳过的测试方法,通过添加xfail装饰器可以在结果中给出明显的标识

新建一个test_xfail.py

import pytest

"""
   # 源码
   def __call__(
   self,
   condition: str | bool = False,  # 主要条件参数,可以是字符串或布尔值,默认值为 False
   *conditions: str | bool,  # 可变参数,允许传递多个条件,每个条件可以是字符串或布尔值
   reason: str = ...,  # 字符串参数,用于说明标记的原因,默认值为未指定
   run: bool = ...,  # 布尔值参数,用于控制是否运行测试,默认值为未指定
   raises: None | type[BaseException] | tuple[type[BaseException], ...] = ...,  # 异常类型参数,用于指定测试是否应该抛出特定异常,默认值为未指定
   strict: bool = ...,  # 布尔值参数,用于控制是否严格匹配异常,默认值为未指定
) -> MarkDecorator:  # 返回一个 MarkDecorator 对象,用于标记测试
   这是一个特殊方法 __call__,用于定义对象被调用时的行为。
   在 pytest 中,它被用于实现标记装饰器(MarkDecorator)的功能。

   参数说明:
   - condition: 主要条件参数,可以是字符串或布尔值。如果条件为 True 或非空字符串,标记会生效。
   - *conditions: 可变参数,允许传递多个条件。所有条件会被组合起来,决定标记是否生效。
   - reason: 字符串参数,用于说明标记的原因。例如,跳过一个测试时,可以提供一个跳过的原因。
   - run: 布尔值参数,用于控制是否运行测试。如果为 False,测试会被跳过。
   - raises: 异常类型参数,用于指定测试是否应该抛出特定异常。如果测试没有抛出指定异常,测试会失败。
   - strict: 布尔值参数,用于控制是否严格匹配异常。如果为 True,测试抛出的异常必须完全匹配 raises 参数指定的异常类型。

   返回值:
   - 返回一个 MarkDecorator 对象,用于标记测试。
"""


@pytest.mark.xfail()
def test_xfail1():
    assert 11 + 1 == 12


@pytest.mark.xfail(reason="运算错误")
def test_xfail2():
    assert 5 + 5 == 11


# condition为True, 一定要加上reason
# strict 参数用于控制是否将 XPASS 视为失败。如果 strict=True,XPASS 会被视为失败
@pytest.mark.xfail(strict=True)
def test_xfail3():
    assert 1 + 1 == 2


strict为True 

 @pytest.mark.patametrize

使用pytest -k 测试方法名  只运行该测试方法

代码:

单个参数

# parametrize 用于对测试方法进行数据参数化,使得同一个测试方法结合
# 不同的测试数据也可以达到同时测试的目的
import pytest

list_one = [1, 2, 3, 4]

"""
1. 如果argnames只有一个名称, 则argvalues要以列表的形式给出值
2. 如果argnames有多个名称,则argvalues需要以列表嵌套元组的形式给出值
3. 如果parametrize的参数名称和fixture一样,会覆盖掉fixture
e.g:
@parametrize('arg1',[1,2]) 将对测试函数调用两次
...第一次调用arg1=1, 第二次调用arg1=2
@parametrize('arg1','arg2',[(1,2),(3,4)] 将对测试函数调用两次
...第一次调用: arg1=1, arg2=2 第二次调用arg1=3, arg2=4
"""
@pytest.mark.parametrize('number', list_one)
def test_parametrize1(number):
    assert number in list_one

多个参数 

# 多个参数
list_two = [(1, 2, 3), (2, 3, 4), (3, 4, 7)]


@pytest.mark.parametrize('num1,num2,sum', list_two)
def test_parametrize2(num1, num2, sum):
    assert num1 + num2 == sum

直接标记 

mark标记是通过在测试方法上添加装饰器进行标记,还有一种标记方法是在命令行中使用参数进行标记

使用参数进行标记的方法有两种:

  1. 第一种是直接标记:只运行某个固定的py文件,或在py文件后添加双引号::运行固定的测试方法
  2. 通过参数-k进行模糊匹配标记

    假设是test.example.py

  3. pytest   test.example.py 只运行这个py文件

 添加双引号,只运行这一个方法

pytest test.example.py::test_add

也可以执行多个方法

模糊匹配标记 

在直接标价中一次只能执行一个测试文件或者方法,如果一次执行多个测试方法可以使用模糊匹配标记,模糊匹配使用起来很简单:

pytest -k 命令 进行匹配标记即可

pytest -v -k  信息更详细一些

 使用mark自定义标记

我们根据mark进行自定义标记,只需在测试方法前添加装饰器 pytest.mark.标记名

标记名建议根据项目取比较容易世标的词,例如:commit, mergerd, done, undo等

在命令模式下使用时:只需要通过参数 -m 加上标记名就可执行被标记的测试方法

创建一个test_customize.py

import pytest


def add_number(a, b):
    return a + b

@pytest.mark.done
def test_add1():
    assert add_number(2, 3) == 5

@pytest.mark.undo
def test_add2():
    assert add_number(2, 3) == 4

@pytest.mark.undo
def test_add3():
    assert add_number(3, 3) == 6

pytest在执行通过 -m 参数只执行被done标记的测试方法,使用命令 pytest -v -m 'done'

 第三章 固件Fixture

简介:

Fixture 中文称为固件或者夹具,用于测试用例执行前的数据准备、环境搭建和测试用例执行后的数据销毁、环境恢复等工作,与单元测试框架UnitTest中的setup、teardown功能类似,但是pytest框架提供的fixture更灵活,不但允许代码在运行时旨在某些特定测试用例前执行,而且还可以在测试用例和测试用例之间传递参数和数据

fixture的使用

如果要将一个方法作为fixture使用,只需要在该方法前添加装饰器@pytest.fixture()即可

import pytest

@pytest.fixture()
def fixture_prepare():
    print("\nthis is fixture prepare")

def test_fixture1(fixture_prepare):
    print('test_fixture1')

def test_fixture2(fixture_prepare):
    print('test_fixture2')


if __name__=='__main__':
    pytest.main(['-s','test.fixture.py'])

执行结果:可以看到每一个case执行之前都执行了fixture_prepare

fixture参数 

  1. scope:定义fixture的作用域,有4组可选参数:function,class, module,package/session,默认为function
  2. params:一个可选的参数列表,会使多个参数调用fixture函数和所有测试使用它
  3. autouse:如果为True,则所有方法都会执行固件方法,如果为False(默认值),则支队天界了固件方法的测试方法执行固件方法
  4. ids:每个参数都与列表中的字符串id对应,因此他们是测试id的一部分,如果没有提供id将会从参数中自动生成
  5. name: Fixture的名称,默认为装饰器的名称,如果Fixture在与他定义的模块中使用,那么这个fixture的功能名称将被请求的fixture功能参数遮盖。可以用该国将装饰函数命名为”fixture_fixturename" 然后使用“@pytest.fixture(name='fixturename')解决这个问题

fixture的作用域 

fixture的作用域用来指定固件使用的范围,固件的使用范围可以通过scope参数进行声明

function:函数级别,默认级别,每个测试方法执行前都会执行一次

class:类界别,每个测试类执行前执行一次

module:模块级别,每个模块执行前执行一次,也就是每个.py文件执行前都会执行一次

session:会话级别,一次测试只执行一次,即多个文件调用一次,可以跨.py文件

import pytest


@pytest.fixture(scope='session')
def sesseion_fixture():
    pass


@pytest.fixture(scope='module')
def module_fixture():
    pass


@pytest.fixture(scope='class')
def class_fixture():
    pass


@pytest.fixture(scope='function')
def function_fixture():
    pass


def test_fixture(sesseion_fixture, module_fixture, class_fixture, function_fixture):
    pass


if __name__ == '__main__':
    pytest.main(['--setup-show', 'fixture_scope.py'])

命令:

pytest --setup-show fixture_scope.py

 

 使用参数 --setup-show(查看具体的setup和teardown)顺序,从图中可以看到明显的作用域表示符号

S:session会话级别

M:module

C:class级别

F:function函数级别

@pytest.mark.usefixtures()

当用例需要调用fixture时,可以用过直接在用例里加fixture参数,如果一个测试class类中所有的测试方法都需要用到fixture,美分用例都去传参会比较麻烦,这个时候选择在class类上使用装饰器,@pytest.mark.usefixtures修饰,使整个class都调用Fixture

在运行 pytest 时,添加 -s 选项以显示 print 语句的输出:

import pytest
from fixture_scope import function_fixture


@pytest.mark.usefixtures('function_fixture')
class TestFixture:
    def test_fixture1(self):
        pass

    def test_fixture2(self):
        pass

注意:Fixture有返回值,而usefixtures无法获取到返回值,则不再适用 

执行结果

autouse(自动使用)

我们可以看到测试方法通过参数名称可以使用测试固件,但是当测试方法特别多时,每次传入参数特别麻烦,为了解决这一问题,Fixture提供了autouse,可以自动将测试固件添加到测试方法上。autouse默认是False, 不启用,如果设置为True,则开启自动使用Fixture功能,着用就不需要每次必须传入参数,即可使用测试固件

import pytest

@pytest.fixture(autouse=True)
def autouse_fixture():
    print('this is autouse of fixture')


def test_fixture1():
    print('this is test_fixture1')


def test_fixture2():
    print('this is test_fixture2')


if __name__ == '__main__':
    pytest.main(['-s', '--setup-show', 'fixture_autouse.py'])

运行结果可以看出:

test_fixture1和test_fixture2两个测试方法的setup和teardown都执行连固件autouse_fixture,与预期结果一致,可见autouse可以自动将测试固件添加到测试方法上

注意:测试固件旨在测试方法   前    进行内容输出

yeild的使用

在UnitTest单元测试框架中,有setup和teardown功能作为数据的准备和销毁,但是autouse的示例中知道,测试固件只在测试方法运行前进行内容输出

而在测试方法完成之后没有任何输出.

如果想在测试结束后执行操作,需要使用yield的配合,如果没有yield就相当于只有setup,没有teardown

import pytest

@pytest.fixture()
def fixture_yield():
    print('\nTest started----------我是setup')
    yield
    print('\nEnd of test------------我是teardown')

def test_yield(fixture_yield):
    print('\n数据销毁测试')

运行命令: pytest -s --setup-show test_yield.py
 

可以看出:测试用例在执行前执行连yield关键词之前的语句,测试用例执行后执行了yield关键词之后的语句

注意点:

 共享Fixture功能

许多测试用例的前置条件都存在相同的内容,比如一个后台管理系统的测试,登录是绕不过去的,如果每一个测试用例都要复写一边登录显然不合理。因此将登录写成一个方法,然后共享给所有需要使用的测试用例即可达到服用的目的;在pytest框架下提供了一个共享fixture的功能

只需要创建一个名为conftest.py的文件,将共享的功能在里面定义,其他测试文件在运行时会自动查找

在使用conftest.py文件时的注意点:

  • conftest.py文件名称需要固定,不能更改
  • conftest.py需要与运行的用例文件在同一个package下,并且存在__init__.py文件
  • 使用conftest.py时不需要import导入,pytest用例会自动识别
  • 如果conftest.py放在项目的根目录下,则对全局生效。如果放在某个package下,则只对package下的用例文件神效,允许存在多个conftest.py文件
  • conftest.py文件不能被其他文件导入
  • 所有同目录测试文件运行前都会执行conftest.py文件

创建文件:FileConftest

conftest.py

# 定义一个登录退出功能,在测试方法运行前进行登录,测试方法结束后退出登录
# 并且给登录退出方法加上Fixture装饰器@pytest.fixture
import pytest


@pytest.fixture()
def signin_signout():
    print('\n成功登陆到系统') #setup
    yield
    print('\n退出系统') #teardown

test_file1:

import pytest

def test_conftest1(signin_signout):
    print("第一个测试方法进行操作")

test_file2:

import pytest

def test_conftest2(signin_signout):
    print("第2个测试方法进行操作")


def test_conftest3(signin_signout):
    print("第3个测试方法进行操作")

 在命令行模式下进入FileConftest目录,使用pytest  -v -s运行脚本,在控制台输出测试结果

conftest.py的登录退出的作用域是函数级别的,所以程序运行中每个测试方法都会执行

如果想过要登录退出功能在程序运行过程中只运行一次,那么只需要将Fixture的作用域改成session 

参数化

在mark标记中已经了解到可以用@pytest.mark.parametrize对测试方法进行参数化,那么在固件中也可以同样对其进行参数化,因为固件Fixture也是函数。与pytest.mark.parametrize不同的是,Fixture参数化是通过参数params实现的

同一个测试方法可能需要不同的参数来构造逻辑基本相同,环境或者结果稍微有所不同的场景,这时候可以利用Fixture的参数化([arametrizing)来减少重复工作。例如测试两个数的和的一个测试方法,则可使用参数化进行测试

import pytest


# params: Iterable[object] | None = None,
@pytest.fixture(params=[(1, 3, 4), (2, 4, 6), (12, 33, 45)])
def test_params(request):
    print(request)
    return request.param

def test_add(test_params):
    assert test_params[2] == test_params[0]+test_params[1]

 执行固件参数化会使用pytest中内置的固件request,并通过request.param来获取参数,代码结束后,可以看到运行了3次,有3组测试数据

内置Fixture

# 如果有空会继续更新,感谢你的支持~ 

后续是:

pytest插件

插件的安装和卸载

查看活动插件

插件的注销

allure测试报告

allure的安装

脚本应用

报告生成

总结

路漫漫其修远兮,后续更新自动化项目实战,一些列子

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

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

相关文章

ubuntu22.04安装RAGFlow配合DeepSeek搭建本地知识库

一、简介 RAGFlow 是一个基于对文档的深入理解的开源 RAG(检索增强生成)引擎。当与 LLM 集成时,它能够提供真实的问答功能,并以来自各种复杂格式数据的有根据的引用为后盾。 二、安装 1.环境要求 CPU ≥ 4 核 (x86…

【银河麒麟高级服务器操作系统实例】虚拟机桥接网络问题分析及处理

更多银河麒麟操作系统产品及技术讨论,欢迎加入银河麒麟操作系统官方论坛 https://forum.kylinos.cn 了解更多银河麒麟操作系统全新产品,请点击访问 麒麟软件产品专区:https://product.kylinos.cn 开发者专区:https://developer…

springboot011基于springboot的课程作业管理系统(源码+包运行+LW+技术指导)

项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得难了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等,你想解决的问题,今天…

Android 屏幕适配 Tips

概念 屏幕尺寸:屏幕的对角线的长度屏幕分辨率:屏幕分辨率是指在横纵向上的像素点数,单位是px,1px1个像素点。一般以纵向像素x横向像素,如1960x1080屏幕像素密度:每英寸上的像素点数,单位是dpi …

使用 Arduino 的 WiFi 控制机器人

使用 Arduino 的 WiFi 控制机器人 这次我们将使用 Arduino 和 Blynk 应用程序制作一个 Wi-Fi 控制的机器人。这款基于 Arduino 的机器人可以使用任何支持 Wi-Fi 的 Android 智能手机进行无线控制。 为了演示 Wi-Fi 控制机器人,我们使用了一个名为“Blynk”的 Android 移动应…

使用 Deepseek + kimi 快速生成PPT

前言 最近看到好多文章和视频都在说,使用 Deepseek 和 kimi 能快速生成精美的 ppt,毕竟那都是别人说的,只有自己尝试一次才知道结果。 具体操作 第一步:访问 deepseek 我们访问 deepseek ,把我们想要输入的内容告诉…

XHR请求解密:抓取动态生成数据的方法

在如今动态页面大行其道的时代,传统的静态页面爬虫已无法满足数据采集需求。尤其是在目标网站通过XHR(XMLHttpRequest)动态加载数据的情况下,如何精准解密XHR请求、捕获动态生成的数据成为关键技术难题。本文将深入剖析XHR请求解密…

C#程序加密与解密Demo程序示例

目录 一、加密程序功能介绍 1、加密用途 2、功能 3、程序说明 4、加密过程 5、授权的注册文件保存方式 二、加密程序使用步骤 1、步骤一 ​编辑2、步骤二 3、步骤三 4、步骤四 三、核心代码说明 1、获取电脑CPU 信息 2、获取硬盘卷标号 3、机器码生成 3、 生成…

MC9S12单片机的内存映射机制

地址空间 这是个16位的单片机。CPU的寻址空间最大为2^1664K。 这个64K是包括外设、RAM、EEPROM、和FLASH的。现在程序越来越大,64K的空间肯定是不够用的。因此,需要扩展。 扩展方法就是:分页。 把原来的64K空间,划分一块出来&a…

计算机毕业设计SpringBoot+Vue.js科研项目验收管理系统(源码+文档+PPT+讲解)

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…

Docker Compose企业示例

利用容器编排完成haproxy和nginx负载均衡架构实施 1.mkdir docker.test 2.touch haproxy.yml 3.mkdir /var/lib/docker/volumes/conf 4.dnf install haproxy -y --downloadonly --downloaddir/xixi:下载内容到/xixi目录下 5. rpm2cpio haproxy-2.4.22-4.el9.x8…

【Linux网络#11】: 传输层协议 TCP

📃个人主页:island1314 🔥个人专栏:Linux—登神长阶 ⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞 生活总是不会一帆风顺&#x…

19. 大数据-技术生态简介

文章目录 前言一、Hadoop介绍1. 简介2. Hadoop发展史3. Hadoop现状 二、Hadoop特性1. Hadoop国外应用2. Hadoop国内应用 三、Hadoop架构变迁1. 发行版本2. Hadoop架构变迁(1.0-2.0变迁)3. Hadoop架构变迁(3.0新版本)4. 综述 四、技术生态体系 前言 大数据(Big Data…

Android Native 之 文件系统挂载

一、文件系统挂载流程概述 二、文件系统挂载流程细节 1、Init启动阶段 众所周知,init进程为android系统的第一个进程,也是native世界的开端,要想让整个android世界能够稳定的运行,文件系统的创建和初始化是必不可少的&#xff…

C++蓝桥杯基础篇(八)

片头 嗨~小伙伴们,大家好!今天我们一起来学习C蓝桥杯基础篇(八),练习相关字符串的习题,准备好了吗?Are you ready? Lets go! 第1题 字符串中的数字个数 这道题,我们用字符数组或者…

IDEA2023 使用枚举类型java: 非法字符: ‘\ufffd‘

一、异常: 二、原因 文件编码问题 IDE或文本编辑器的文件编码设置不正确,可能会导致在保存文件时引入了错误的字符。 三、解决 在IntelliJ IDEA中,你可以通过File -> Settings -> Editor -> File Encodings来设置。

3.6 登录认证

登录功能 登录思路 联调测试 登录校验 问题:在未登录情况下,我们也可以直接访问部门管理、员工管理等功能。 登录标记 用户登录成功之后,每一次请求中,都可以得到该标记。 统一拦截 过滤器Filter拦截器Interceptor 会话技术 会…

qt-C++笔记之ubuntu22.04源码安装Qt6.8.2

qt-C++笔记之ubuntu22.04源码安装Qt6.8.2 code review! 文章目录 qt-C++笔记之ubuntu22.04源码安装Qt6.8.21.作者环境:ubuntu22.04、cmake202.安装3.关联已安装的 Qt6 到 Qt Creator4.附:ubuntu18.0的处理,可尝试,作者没有遇到这个问题1.作者环境:ubuntu22.04、cmake20 安…

简单的二元语言模型bigram实现

内容总结归纳自视频:【珍藏】从头开始用代码构建GPT - 大神Andrej Karpathy 的“神经网络从Zero到Hero 系列”之七_哔哩哔哩_bilibili 项目:https://github.com/karpathy/ng-video-lecture Bigram模型是基于当前Token预测下一个Token的模型。例如&#x…

计算机视觉之dlib人脸关键点绘制及微笑测试

dlib人脸关键点绘制及微笑测试 目录 dlib人脸关键点绘制及微笑测试1 dlib人脸关键点1.1 dlib1.2 人脸关键点检测1.3 检测模型1.4 凸包1.5 笑容检测1.6 函数 2 人脸检测代码2.1 关键点绘制2.2 关键点连线2.3 微笑检测 1 dlib人脸关键点 1.1 dlib dlib 是一个强大的机器学习库&a…