Python教程

python单元测试框架笔记

本文主要是介绍python单元测试框架笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录
  • 单元测试概述
    • 什么是单元测试
    • 单元测试什么进行?
    • 单元测试由谁负责?
    • 单元测试需要注意
    • 单元测试覆盖类型
  • python 单元测试框架
    • unittest
    • pytest 测试框架

单元测试概述

什么是单元测试

单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小很明确的功能是否正确。通常而言,一个单元测试是判断某个特定条件(或者场景)下某个特定函数的行为。

单元测试什么进行?

单元测试越早对后期的集成测试越有好处

单元测试由谁负责?

开发者自己负责

单元测试需要注意

单元测试的时候一个大前提就是需要清楚的知道,自己要测试的程序块所预期的输入输出,然后根据这个预期和程序逻辑来书写 case。这里的预期结果一定要针对需求/设计的逻辑去写,而不是针对程序实现去写,否则单元测试就失去了意义,照着错误的实现设计出的 case 也可能是错的。单元覆盖率
代码覆盖率也被用于自动化测试和手工测试来度量测试是否全面的指标之一,应用覆盖率的思想增强测试用例的设计

单元测试覆盖类型

待测试的代码片段

def demo_method(a,b,x):
	if (a>1 and b==0):
  	x=x/a
  if (a==2 or x>1
      x= x+1
   return x 

语句覆盖

  • 定义
    通过设计一定量的测试用例,保证被测试的方法每一行都会执行一遍。运行测试用例的时候被击中的代码行即称为被覆盖的语句
  • 测试用例
    需要一条 case,即可实现行覆盖:a=10,b=0,x=3
    漏洞 and -> or:第一个判断 if 内的 and 改为 or 后此测试用例可以通过
    行覆盖是一个最基础的覆盖方式,但是也是最薄弱的,入过完全依赖行覆盖,就会出现严重的问题
    判断覆盖
  • 定义:运行测试用例过程中被击中的判定语句
    测试用例
    image

漏洞:
大部分的判定语句是由多个逻辑条件组合而成,若仅仅判断其整个最终结果,而忽视每个取值条件的结果,必然会遗漏部分测试路径
a ==2 or x>1 ---> a==2 or x<1
条件覆盖

  • 定义: 条件覆盖和判定覆盖类似,不过判定覆盖关注整个判定语句而条件覆盖关注某个判定条件

测试用例: if (a >1 and b ==0)
image
缺陷:测试用例指数级增加(2**conditions)
路径覆盖

  • 定义: 覆盖所有可能执行的路径
    测试路径
    image
    测试用例
    image
    可以利用桩代码的技术帮助实现路径覆盖

python 单元测试框架

unittest:Python 内置的标准库。它的 API 跟 Java 的 JUnit,.net 的 NUnit,C++的 CppUnit
pytest:丰富,灵活的测试框架,语法简单,可以结合 allure 生成一个酷炫的测试报告
Nose: unittest 的扩展,使得 python 的测试更加简单
Mock:unittest.mock 是用来测试 python 的库,这个是一个标准库(出现在 python 3.3 以后)

unittest

python 自带的单元测试框架,常用在单元测试
在自动化测试中提供用例组织与执行
提供丰富的断言---验证函数等功能
加上 HTMLTestRunner 可以生成 HTML 的报告

unittest 编写规范

  • Unittest 提供了 test cases,test suite, test fixtures, test runner 相关的组件
  • 测试模块首先 import unittest
  • 测试类必须集成 unittest.TestCase
  • 测试方法必须以“.test_”开头
  • 模块名字,类名没有特殊要求
    unittest 测试框架结构
  • SetUp 用来准备测试环境,tearDown 用来清理环境
  • 如果想要在所有 case 执行之前准备一次环境,并在所有 case 执行结束后再清理环境,我们可以使用 setUpClass(),与 tearDownClass()
  • 如果有些方法不在本次执行使用 @unittest.skip
  • 测试方法的命名:以 test 开头

各种执行行-单一用例, 全部

import unittest
class TestClass(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print("这个测试整个类前要执行的方法")
    def setUp(self):
        print("这是每一个类创建的方法")
    def tearDown(cls):
        print("这是每一个方法后面运行的方法")
    def test_first(self):
        print("这是测试方法1")
        self.assertEqual(1,1)
    @unittest.skip("这次不想执行这个测试")
    def test_second(self):
        print("这是测试方法2")
        self.assertEqual(1,'1',"1 is not equal '1'")
    @classmethod
    def tearDownClass(cls):
        print("这是测试整个类后要执行的方法")
if __name__ == '__main__':
    unittest.main()

assert 断言
python3---assert 官方文档:https://docs.python.org/3/library/unittest.html#assert-methods
unittest 执行测试用例
多个测试用例的集合就是测试套件,通过测试套件来管理多个测试用例

执行方法 1
unittest.main()

执行方法 2: 加入容器中执行

suite.addTest(TestMethod("test_01"))

suite.addTest(TestMethod(("test_02"))

unittest.TextRunner().run(suite)

测试方法三:此用法可以同时测试多个类

suite1 = unittest.TestLoader().loadTestsFromTestsFromTestCase(TestCase1)

suite2 = unittest.TestLoader().loadTestsFromTestsFromTestCase(TestCase2)

suite = unitest.TestSuite([suite1,suite2])

unitest.TextTestRunner(verbosity=2).run(suite)

执行方法四:匹配某个目录下所有以 test 开头的 py 文件,执行这些文件下所有测试用例

test_dir ="./test_case"

discover  = unittest.defaultTestLoader.discover(test_dir,pattern="test*.py"

discover 可以一次调用多个脚本

test_dir 被测试脚本的路径

pattern  脚本名称匹配规则

测试用例执行过程

  1. 写好 TestCase
  2. TestLoader 加载 TestCase 到 TestSuite
  3. TextRunner 来运行 TestSuite
    运行结果保存在 TextResult 中
    整个过程集成在 unittest.main 模块中
    TestCase 可以是多个,TestSuite 也可以是多个
    生成测试报告
    HTMLTestRunner_py2:http://tungwaiyip.info/software/HTMLTestRunner.html
    HTMLTestRunner_py3:https://github.com/huilansame/HTMLTestRunner_PY3
    image

pytest 测试框架

pytest 框架介绍
pytest 是一个非常成熟的全功能的 python 测试框架
简单灵活,容易上手
支持参数化
测试用例的 skip 和 xfail,自动失败重试
能够支持简单的单元测试和复杂的功能测试,还可以用来做 selenium/appium 等自动化测试,接口自动化测试(pytest +requests)
pytest 具有很多第三方插件,并且可以自定义扩展,比较好的如 pytest-allure
可以很好的和 jenkins 集成
官方文档
文档:Full pytest documentation — pytest documentation

第三方库:Search results · PyPI

pytest 简单练习

# 文件名 test_simple.py
def func(x):
    return x+1
def test_answer():
    assert func(3)==5

pytest .\test_simple.py
pytest .\test_simple.py
pip install -U pytest U 表示升级
pip install pytest pytest-sugar
pip install pytest-rerunfailures
pip install pytest-xdist
pip install pytest-assume
pip install pytest-html
pip list 查看
pytest -h 帮助

测试用例的识别与运行

测试文件

  • test_*.py
  • *_test.py

用例识别

  • Test类包含所有 test_的方法(测试类不能带有__init__方法)
  • 不在 class 中的所有的 test_* 方法
    pytest 也可以执行 unittest 框架写的用例和方法
    终端执行
pytest/py.test

pytest -v (最高级别信息--verbose)打印详细运行日志信息

pytest -v -s 文件名(s 是带控制台输出结果,也是详细输出)

pytest 文件名.py   执行单独一个 pytest 模块

pytest 文件名.py::类名      运行某个模块里面某个类

pytest 文件名.py:: 类名::方法名   运行某个模块里面的某个类里面的方法

pytest -v -k "类名 and or 方法名"  跳过某个用例

pytest -m [标记名]   @ Mark.[标记名]  将运行有这个标记的测试用例

pytest -x 文件名  一旦运行报错就停止运行

pytest --maxfail==[num]  当运行错误达到 num 的时候就停止运行

Pytest 执行--失败重新运行

场景: 测试失败后要重新运行 n 次,要在重新运行之间添加延迟时间,间隔 n 秒再运行
pip install pytest-rerunfailures

pytest -v --reruns 3 -s test_class.py

pytest -v - -reruns 5 --returns-delay 1
多条断言有失败也都运行
场景:一个方法中写多条断言,通常第一条过不去,下面就不执行了。我们想报错也都执行一下。

pip install pytest-assume

pytest.assume(1==4)

pytest.assume(2==4)

测试用例的识别与运行

pycharm 配置与执行 pytest 测试框架
image
image

pytest 框架结构
import pytest 类似的 setup, teardown 同样更灵活

  • 模块级(setup_module/ teardown_module)模块始末,全局的(优先级最高)

  • 函数级(setup_function/teardown_function) 只对函数用例生效(不在类中)

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

  • 方法级 (setup_method/teardown_method) 开始于方法始末(在类中)

  • 类里面的(Setup/teardown)运行在调用方法的前后

pytest-fixture 的用法
场景

  • 用例 1 需要先登录,用例 2 不需要登录,用例 3 需要登录

在方法前面 @pytest.fixture()
例子 1:前端自动化中应用
场景:测试用例执行时,有的用例需要登录才能执行,有些用例不需要登录。setup 和 teardown 无法满足,fixture 可以。,默认 scope function

  1. 导入 pytest
  2. 在登录的函数上加 @pytest.fixture()
  3. 在要使用的测试方法中传入(登录函数名称),就先登陆
  4. 不传入的就不登录直接执行测试方法
    image
    image
  5. 在测试团队合作中,可以在 conftest.py 中写公共的方法(pytest)
    例子 2:前端自动化中应用---conftest

场景:与其他测试工程师合作一起开发时,公共模块要在不同文件中,要在大家都能访问到的地方

  1. conftest.py 这个文件进行数据共享,并且他可以放在不同位置起着不同的范围共享作用
  2. 系统执行到参数 Login 时先从文本文件中查找是否有这个名字的变量,之后在 conftest.py 中找是否有

步骤:

将登录模块带 @pytest.fixture 写在 conftest.py

conftest.py 配置需注意:

conftest 文件名是不能换的

conftest.py 与运行的用例要在同一个 package 下,并且有__init__.py

不需要 import 导入 conftest.py ,pytest 用例会自动查找

全局的配置和前期工作都可以写在这里,放到某个包下,就是这个包数据共享的地方

例子 3: 前端自动化中应用-yield

场景:

你已经可以将测试方法前要执行的或的依赖解决了,测试方法后销毁清除数据要如何进行呢?

解决

通过在同一模块中加入 yield 关键字,yield 是调用第一次返回结果,第二次执行它下面的语句返回

步骤

在 @pytest.fixture(scope= module)

在登录的方法中加 yield ,之后加销毁清除的步骤,(这种方法没有返回值,如果希望返回使用 addfinalizer)

fixture 的自动应用

场景

不想源测试方法有任何改动,或全部都自动实现自动应用,每特例,也都不需要返回值时可以选择自动应用

解决

使用 fixture 中的 autouse = True 实现

步骤

在方法上加 @pytest.fixture(autouse = True)

在测试方法上加 @pytest.mark.usefixtures("start")

从结果中可以看到每次测试方法的执行软件都执行了 open 函数

fixture 带参数传递

场景

测试离不开数据,为了数据灵活,一般数据购书通过参数的

解决:fixture 通过固定参数 request 传递

步骤

在 fixture 中增加 @pytest.fixture(params=[1,2,3,'linda']在参数写 request

import pytest

@pytest.mark.parametrize("test_input,expected",[{"3+5",8},("2+5",7),("7+5",30)])
def test_eval(test_input,expected):
assert eval(test_input) == expected

import pytest

test_user_data =['Toma','Jerry']
@pytest.fixture(scope="module")
def login_r(request):
# 这是接收并传入参数
user = request.param
print( f"\n 打开首页准备登录,登录用户:{user}")
return user
indirect = true 可以把传过来的参数当函数来执行
@pytest.mark.parametrize("login_r",test_user_data,indirect=True)
def test_login(login_r):
a = login_r
print(f"测试用例中login的返回值:{a}")
assert a!= ""

skip 跳过

xfail

mark 中的 skip 与 xfail

skip 使用场景

调试是不想运行这个用例

标记无法在某些平台上运行的测试功能

在某些版本中执行,其他版本跳过

当前的外部资源不可用时跳过(如果测试数据是从数据库中取得的,连接数据库的功能如果返回结果未成功就跳过,因此执行也都报错)

解决

@pytest.mark.skip 跳过这个测试用例,可以加上条件 skipif, 在某些条件下才希望通过,否则就跳过这个测试

Xfail 场景

功能测试尚实施或尚未修复的错误,当前测试通过时,尽管预计会失败(标记为 pytest.xfail, 它是一个 xpass ,将在测试摘要中报告

你希望测试由于某种情况而应该失败

解决

@pytest.mark.xfail

使用自定义标记 mark 只执行某部分用例

场景

只执行符合要求的某一部分用例,可以把一个 web 项目划分为多个模块,然后指定模块名称执行

app 自动化是,如果想 Android 和 IOS 共用一套代码时,可以使用标记功能标记那些是 Android 的,那些是 iOS 的运行时指定 Mark 名称即可

解决

在测试用例方法上加上 @pytest.mark.webtest

执行

-s 参数:输出所有测试用的 print 信息

-m :执行自定义标记的相关用例 pytest -s test_mark_zi_09.py

pytest -s test-mark_zi_09.py -m=webtest

pytest -s test_mark_zi_09.py -m apptest

pytest -s test_mark_zi_09.py -m "not ios"

多线程并行执行与分布式执行

场景

测试 1000 条,一个用例执行 1 分钟,一个测试员执行需要 1000 分钟,通常我们会用人力成本换取时间成本,加几个人一起执行,时间就会缩短。如果 10 个人一起执行只需要 100 分钟,这就是一种并行测试,分布式场景

解决

pytest 分布式插件:pytest-xdist,多个 CPU 或主机执行 前提:用例之间是独立的,没有先后顺序,随机都能执行,可重复运行并不影响其他用例

安装:

pip3 install pytest-xdist

多个 CPU 并行执行用例,直接加-n 3 是并行数量 pytest -n 3

在多个终端下一起执行

pytest-html 生成报告

安装

pip imstall pytest-html

https://pypi.org/project/pytest-html/

生成 html 报告

pytest -v -d --html - - self-contained-html

参数化用例
pytest 数据参数化

@python.mark.parametrize (argnames, argvalue)

argnames:要参数化的变量,string(逗号分割) list tuple

使用 string

@pytest.mark.parametrize("a,b",[(10,20),(10,30)]
def test_param(self,a,b):
print(a+b)
使用 list

@pytest.mark.parametrize(["a","b"],[10,20],[10,30])
def test_param(self,a,b):
print(a+b)
使用 tuple

@pytest.mark.parametrize(("a","b"),[10,20],[10,30])
def test_param(self,a,b):
print(a+b)

class Testdata:
# @pytest.mark.parametrize("a,b",[(10,20),(10,5),(3,9)])
# @pytest.mark.parametrize(("a","b"),[(10,20),(10,5),(3,9)])
@pytest.mark.parametrize(["a","b"],[(10,20),(10,5),(3,9)])
# ["a","b"] 可以进行修改
# ("a","b") 可以进行修改

def test_data(self,a,b):
    # a=10
    # b=20
    print(a+b)

argvalues: 参数化的值,list, list[tuple]

yaml 的基本使用

yaml 实现 list

list

  • 10

  • 20

  • 30

yaml 实现字典

dict

by:id

locator:name

action:click

yaml 进行嵌套

  • by:id

-locator:name

  • action:click

yaml 进行更复杂的嵌套

companies:

id:1

name: company1

price:200w

id:2

name:company2

price:500w

companies:[{id:1,name:company,price:200w},{id:2,name:company2,price:500w}]

加载 yaml 文件

yaml.safe_load(open("./data.yaml"))

class Testdata_yaml:
@pytest.mark.parametrize(("a","b"),yaml.safe_load(open("./data.yaml")))
def test_data_yml(self,a,b):
print(a+b)
pip install PyYaml

data.yml

运行结果

数据驱动
简介

数据驱动就是数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变(参数化的应用)

数据量大的测试用例可以使用一种结构 haul 的文件(yml, JSON)来对数据进行存储,然后再测试用例中读取这些数据

简单练习

import pytest
import yaml

class TestDemo:
@pytest.mark.parametrize("env",yaml.safe_load(open("./env.yml")))
def test_demo(self,env):
if "test" in env:
print("这是测试环境")
print("测试环境的IP是:",env["test"])
elif "dev" in env:
print("这是开发环境")
print("开发环境的IP是:",env["dev"])
def test_yaml(self):
print(yaml.safe_load(open("./env.yml")))

应用场景

App, Web,接口自动化测试

测试步骤的数据驱动

测试数据的数据驱动

配置的数据驱动

测试报告美化
Allure 介绍

Allure 是一个轻量级,灵活的,支持多语言的测试报告工具

多平台,奢华的 report 框架

可以为 dev/qa 提供详尽的测试报告,测试步骤 log

也可以为管理层提供 high level 统计报告

Java 语言开发的,支持跑 pytest,javascript,PHP,ruby

可以继承到 jenkins

allure 安装

Windows/mac 通用安装方法

Releases · allure-framework/allure2 · GitHub

解压---> 进入 bin 目录---> 运行 allure.bat

把 bin 目录 加入 PATH 环境变量

Mac 可以使用 brew 安装

brew install allure

官网 :Allure Framework (qameta.io)

使用 allure2 生成精美报告

安装 allure-pytest

pip install allure-pytest

运行:

在测试执行期间收集结果

pytest[测试文件] -s -q --alluredir= ./alluredir =./result/ (--alluredir 这个选项用于指定存储测试结果的路径)

查看测试报告

测试完成后查看实际报告,在线看报告,会直接打开默认浏览器展示当前报告

allure serve ./result/

从结果生成报告,这是一个启动 Tomcat 的的服务,需要两个步骤:生成报告,打开报告

生成报告:allure generate ./result/5 -o ./report/ --clean (注意:覆盖路径加 --clean)

打开报告: allure open -h 127.0.0.1 -p 8883 ./report/

Allure 常用特性

场景

希望在报告中看到的功能,子功能或场景,测试步骤,包括测试附加信息

解决:

@Feature, @story, @step, @attach

步骤:

import allure

功能上加 @allure.feature("功能名称")

子功能上加 @allure.story("子功能名称")

步骤上加 @allure.step("步骤细节")

@allure.attach("具体文本信息"),需要附加的信息,可以是数据,文本,图片,视频网页

如果只是测试登录功能运行的时候可以添加限制过滤:

pytest 文件名 --allure-features "购物车功能" --allure-storws "加入购物车"

allure 特性-feature/story

注解 @allure.feature 与 @allure.store 的关系

feature 相当于一个功能,一个大的模块,将 case 分类到某个 feature 中,报告中 behaviore 中显示,相当于 testsuite

story 相当于对应这个功能或者模块下的不同场景,分支功能,属于 feature 之下的结构,报告在 features 中显示,相当于 testcase

feature 于 story 类似与父子关系

allure 特性-step

测试过程中每个步骤,一般都放在具体逻辑方法中

可以放在关键步骤中,在报告中显示

在 app,web 自动化测试当中,建议每切换到一个新的页面当做一个 step

用法:

@allure.step() 只能以装饰器的形式放在类或者方法上面

with allure.step(): 可以放在测试用例方法里面,但测试步骤的代码需要该语句包含

allure 特性-issue, testcase

关联测试用例(可以直接给测试用例地址链接)

关联 bug

执行的时候需要加个参数

--allure-link-pattern==issue:http://www.mytesttracker.com/issue{}

代码

@allure.issue('140','Pytest-flaky test retires shows like steps')
def test_with_issue_link():
pass
TEST_CASE_LINK ='https://github.com/qameta/allure-integrations/issuecomment-268313637'
@allure.testcase(TEST_CASE_LINK,'Test case title')
def test_with_testcase_link():
pass
按重要级别进行一定范围测试

场景

通常测试有 p0、冒烟测试、验证验证上线测试。按重要级别来分别执行的,比如上线要把主流程和重要模块都跑一遍

解决

通过 pytest.mark 标记

通过 allure.feature, allure.story

也可以通过 allure.severity 来附加标记

级别:Trivial : 不重要,Minor 不太重要,Normal :正常问题,Critical:严重,Blocker:阻塞

Blocker 级别:中断缺陷(客户端程序无响应,无法执行下一步操作)

Critical 级别:临界缺陷(功能点缺失)

Minor 级别:次要缺陷(界面错误与 UI 需求不符)

    -Trival 级别:轻微缺陷(必输项无提示,或者提示不规范)

步骤

在方法,函数和类上面加

@allure.servity(allure.severity_level.TRIVIAL)

执行时

pytest -s -v 文件名 --allure-severities normal.critycal

前端自动化测试---截图

场景

前端自动化测试经常需要附加图片或 html,在适当的地方,适当的时机截图

解决

allure.attach(body(内容),name, attachment_type,extension);

allure.attach(’首页‘,’这是错误页的结果信息‘ ,allure.attachment_type.HTML)

在测试报告里附加图片:

allure.attach.file(source,name,attachment_type,extension):

allure.attach.file("./result/b.png",attachment_type=allure.attachment_type.PNG)

这篇关于python单元测试框架笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!