参考文章

使用 pytest

pytest 这个 库是一个第三方库,严格来说,它的设计思路不属于 xUnit 系列。但它使用起来比较方便,同时他又兼容 unittest 的用例:用 unittest 写的测试脚本可以用 pytest 来执行。

这种兼容性的设计,在测试执行器的设计思路层面上很普遍。举个例子,几乎所有测试执行器,都兼容 junit 的测试报告,他们都可以输出一种 最初由 junit 提供的 xml 测试报告(有些测试执行器是原生自带这个功能,有些是用插件实现这个功能)。兼容现有工具,有利于新工具的推广,因此各种比较知名的测试执行器都会从设计上就考虑兼容性。

言归正传,接下来介绍 测试执行器的几个重点功能,以及我们怎样用 pytest 里的这些功能。

任务1.安装 pytest

和其他python 库一样我们使用 pip install pytest 来安装pytest

任务2.打开官方文档

官方文档在这里,遇到问题时可以查阅

测试命名规范

pytest 里,测试用例的定义较 unittest 做了简化。

1.类名规范取消,不用继承任何类

上一节的例子中,

我们使用unittest时,需要把测试写在类里,这个类还必须继承 unittest.TestCase 像这样:

class TestStringMethods(unittest.TestCase):

只有继承了 unittest.TestCase 这个类,unittest 才能找到这个类里我们写的测试方法。

而pytest 里,不再强制要求把测试写在类里,也不需要继承任何类。

取而代之的,是使用文件名规范来让pytest 找到我们写的测试方法的文件。

2.文件名以 test_ 开头,注意带下划线

例1.test_1540.py,一个pytest的例子。注意文件名。

import pytestdef inc(x):
   return x + 1def test_answer():
   assert inc(3) == 5if __name__ == '__main__':
   pytest.main()

我们一起来看一下这个例子:

首先第一行,导入 pytest 库

第2-3 行,定义了一个 inc 方法,这个方法会把传入参数加1,再返回。

第4-5 行,定义了一个 test 方法,这一点和unittest 一样,测试方法名要以 test 开头

第6-7行, 定义了程序的入口,这两行可以省略

断言

官方文档中告诉我们,pytest的断言里只要用assert 就行了,不需要 self.assertXXXX。

pytest 会显示这样 的错误信息给我们,以下为例1的运行结果:

============================FAILURES===============
_______________________________ test_answer ________________________________    def test_answer():
>       assert inc(3) == 5
E       assert 4 == 5
E        +  where 4 = inc(3) test_1540.py:7: AssertionError
=================== 1 failed in 0.07 seconds=================

并且上一节中我们用过的自定义更详细的错误信息的方法在这里仍然适用。

setup和teardown

所谓 setup 和 teardown,也是 xUnit 系列测试执行器中的概念。比如,假设我们有3个测试方法,都是操作在线购物网站的购物车的测试,他们有一个共同的前提条件,就是用户需要先登录。那么通常在 xUnit 系列的测试执行器中,测试脚本我们会这样写:

上图示意了一个带有 setup 和teardown 操作的测试套件(test suite)的内部逻辑,这里的 setup 和teardown 是对测试套件的,同样也可以定义对 Case 的和对测试方法的 setup 和teardown 。

当执行一个测试套件时,其顺序是:

套件的 steup====》

case1 的setup====》 case1 的测试方法====》 case1的teardown ====》

case2 的setup====》 case2 的测试方法====》 case2的teardown ====》

case3 的setup====》 case3 的测试方法====》 case3的teardown ====》

套件的 teardown

在pytest中,也支持上述的传统 setup 和 teardown,感兴趣的同学可以看官方文档:

https://docs.pytest.org/en/latest/xunit_setup.html#xunitsetup

本文中,将介绍 pytest 的fixture 以及用fixture实现的 setup和 teardown

Pytest的Fixture

fixture 是什么?

我们可以理解成 fixture 是提供给测试方法用的提前准备好的对象。

举个例子,我们做网页测试,需要先打开一个浏览器,后续所有操作都是在这个浏览器上做的。 fixture 能做的就是给我们的每个测试方法,都准备好一个浏览器对象。

同样,我们做一些测试时,需要先读取一个 excel表格,然后所有测试方法,都需要用这个表格里的某些数据,那么fixture能做的就是给每个测试方法,都准备好一个已经读取完毕的 excel 表格对象。

我们一起来看一个官网的例子:

例2.官网给的 fixture 例子

# content of conftest.py
import pytest import smtplib  
@pytest.fixture(scope="module") def smtp_connection():    
   return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
# content of test_module.py  
def test_ehlo(smtp_connection):    
   response, msg = smtp_connection.ehlo()    
   assert response == 250    
   assert b"smtp.gmail.com" in msg    
   assert 0  # for demo purposes  
def test_noop(smtp_connection):    
   response, msg = smtp_connection.noop()    
   assert response == 250    
   assert 0  # for demo purposes

这个例子里涉及了 conftest.py 和 test_module.py 两个文件

在conftest.py 中,定义了一个 smtp_connection 方法,这个方法使用 smtplib 这个库去建立了一个 gmail 的链接,也就是这两行

def smtp_connection():    
   return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)

而 @pytest.fixture(scope="module") 这一行表示后面紧跟的 smtp_connection 方法是一个fixutre,并且范围是整个module。 范围是 module,则表示这个fixture 在每个module 只会运行一次。在这里,module的 概念和测试套件差不多。本例中,整个 module 也就只有两个测试方法。  也就是说:

这个 smtp_connection 方法在这次整个测试中只会被执行一次。换句话说,它就相当于是 整个测试套件的 setup 方法了。

在 test_module.py 中,定义了两个测试方法,这两个测试方法的共同点是,传入参数里都有 smtp_connection。 没错,这里的smtp_connection就是 conftest中的 smtp_connection 的返回值。

虽然官网的例子我们无法运行,但是我们可以一起来回顾一下整个测试执行过程:

1.先找到所有 test_开头的文件,称为测试脚本文件

2.在测试脚本文件同一级目录下寻找conftest.py,称为测试配置文件

3.按随机顺序执行测试脚本文件中的测试方法

4.执行第一个测试方法,发现有一个传入参数 smtp_connection,在测试配置文件中寻找名为 smtp_connection 的fixture

5.执行测试配置文件中的 smtp_connection 方法,保存返回值

6.把上一步的返回值代入第4步的测试方法传入参数中,执行第一个测试方法

7.执行第二个测试方法,发现有一个传入参数 smtp_connection ,在测试配置文件中寻找名为 smtp_connection 的fixture

8.发现这个fixture的范围是module,无需重复执行,使用第5步的返回值继续执行第7步的第二个测试方法。

理解了上述流程,我们发现, fixture 其实就相当于是 setup方法,并且更灵活:

通过修改 fixture 的 scope (它的值可以是 module,class或fucntion)我们可以给每个方法、每个类定制不同的fixture。 同样,fixture其实也可以定义teardown方法。

例3.在官网例子上增加 teardown

@pytest.fixture(scope="module")
def smtp_connection():    
   yield smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
   print("我就是 teardown,我在测试方法结束后运行")

这个例子中,第四行的 return 改成了 yield。而 第五行开始的内容就会在测试方法执行结束后运行了。相当于是实现了teardown。

下面一起看一个可以运行的例子:

例4.一个用fixture 实现测试方法级别的setup和teardown的例子:

#conftest.py的内容
import pytest @pytest.fixture(scope="function",autouse=True)
def foo():
   print(" function setup")
   yield 100
   print(" function teardown") #test_1540.py的内容
import pytest def inc(x):
   return x + 1 def test_answer_1():
   assert inc(3) == 5 def test_answer_2(foo):
   print(foo)
   assert inc(98) == foo if __name__ == '__main__':
   pytest.main()

一起来看一下这个例子,

在文件conftest.py里,

第2行,使用了装饰器 pytest.fixture,这个装饰器带的参数值表示这个fixture的生效范围是方法级(scope= “function”),也就是说每个方法之前之后都会运行它。并且会自动使用(autouse=True),这个自动使用为真时,我们在测试方法的传入参数表里可以省略这个 fixture的方法名。当然,如果在传入参数里省略了foo,那么就无法使用 foo的返回值。所以一般要自动使用的fixture都是没有返回值的。

第3-6行定义了这个fixture foo,并且返回值固定为100。 返回值 使用 yield 来返回,这样 yield 后的语句会在 测试方法执行后被执行。

运行这个例子的结果如下:

rootdir: C:\Users\colin.zt\Desktop\Homework_20180914\15.0, inifile:
collected 2 items                                                               test_1540.py FF                                                          [100%] ================ FAILURES==========================
________________________________ test_answer_1 ________________________________    def test_answer_1():
>       assert inc(3) == 5
E       assert 4 == 5
E        +  where 4 = inc(3) test_1540.py:9: AssertionError
---------------------------- Captured stdout setup ----------------------------
function setup  
-------------------------- Captured stdout teardown ---------------------------
function teardown
________________________________ test_answer_2 ________________________________ foo = 100    def test_answer_2(foo):
       print(foo)
>       assert inc(98) == foo
E       assert 99 == 100
E        +  where 99 = inc(98) test_1540.py:14: AssertionError
---------------------------- Captured stdout setup ----------------------------
function setup
---------------------------- Captured stdout call -----------------------------
100
-------------------------- Captured stdout teardown ---------------------------
function teardown
========== 2 failed in 0.08 seconds ================

其中需要说明的部分是:Captured stdout setup 和   Captured stdout teardown 这两部分是 pytest 抓取的 setup 和teardown部分的日志,其内容是我们在 foo方法里输出的内容。可以看到上述结果中,共抓到了两次 setup和两次 teardown,这是因为我们的foo方法 范围是 function,而我们有两个测试方法。因此每个测试方法前后都会执行 foo方法的对应语句。

另外,def test_answer_1(): 这个方法里没有显式传入参数 foo,但因为 foo的autouse = True,所以test_answer_1方法执行前后也会执行 foo 方法。

而 def test_answer_2(foo):里显式传入了参数 foo,那么除了执行foo 方法以外,传输参数 foo 还会带有foo 方法的返回值,即 100。

pytest进阶的更多相关文章

  1. pytest进阶之配置文件

    前言 pytest配置文件能够改变pytest框架代码的运行规则.比如修改pytest收集用例的规则,添加命令行参数等等!下面我们来一一讲解常用的一些配置项 Help 通过命令pytest --hel ...

  2. pytest进阶之html测试报告

    前言 Pytest系列已经写了几篇文章了,也不知道对多少人有帮助,总之对于我自己来说该掌握的都已经掌握了,那么今天我们再来说说pytest如何生成一个完整的html测试报告,让你在吹牛逼的路上再多一份 ...

  3. pytest进阶之xunit fixture

    前言 今天我们再说一下pytest框架和unittest框架相同的fixture的使用, 了解unittest的同学应该知道我们在初始化环境和销毁工作时,unittest使用的是setUp,tearD ...

  4. pytest进阶之conftest.py

    前言 前面几篇随笔基本上已经了解了pytest 命令使用,收集用例,finxture使用及作用范围,今天简单介绍一下conftest.py文件的作用和实际项目中如是使用此文件! 实例场景 首先们思考这 ...

  5. pytest进阶之fixture

    前言 学pytest就不得不说fixture,fixture是pytest的精髓所在,就像unittest中的setup和teardown一样,如果不学fixture那么使用pytest和使用unit ...

  6. Pytest - 进阶功能fixture

    1. 概述 Pytest的fixture功能灵活好用,支持参数设置,便于进行多用例测试,简单便捷,颇有pythonic.如果要深入学习pytest,必学fixture. fixture函数的作用: 完 ...

  7. Pytest进阶之参数化

    前言 unittest单元测试框架使用DDT进行数据驱动测试,那么身为功能更加强大且更加灵活的Pytest框架怎么可能没有数据驱动的概念呢?其实Pytest是使用@pytest.mark.parame ...

  8. pytest进阶之fixture函数

    fixture函数存在意义 与python自带的unitest测试框架中的setup.teardown类似,pytest提供了fixture函数用以在测试执行前和执行后进行必要的准备和清理工作.但是相 ...

  9. pytest进阶使用【fixture(一)fixture与setup/teardown区别】

    fixture翻译为装置. 我觉得名字是很贴合功能的,可以自由给函数装置上自己想要的功能. 当在说pytest比unitest灵活时,fixture肯定是其中的一个理由. 测试数据的准备和执行以后的数 ...

随机推荐

  1. Redis学习系列三List列表

    一.简介 Redis中的列表相当于C#中的LinkedList,也就是链表,如果你研究过链表这个数据结构,肯定知道.它的插入和删除是非常快的,但是定位却很慢,因为必须遍历所有的元素,才能找到对应的值, ...

  2. 联系动词Link.V笔记

    这篇单独记录一下联系动词的语法.需要注意的是,只有这个单词在作为联系动词的时候才是这个意思或者才是这样的用法.当然每个单词都会有很多的用法,并不只是这样而已. 第一组:好像…似乎… seem to b ...

  3. Go的方法集

    方法集定义了接口的接受规则. package main import "fmt" type notifier interface { notify() } type user st ...

  4. 第4章 Selenium2-java WebDriver API (一)

    4.1  从定位元素开始 WebDriver提供了八种元素定位方:   在Java语言中对应的定位方法: ·id findElement(By.id())        ·name findEleme ...

  5. CRM项目测试第一天

    经过前几天代码的修改,界面的完善.主要的功能都实现了!今天主要是交换各组的项目,互相来测试,找bug. 在互相测试的过程,我听见有一组应该算是讨论的比价激烈的!我们组我们自己找到了bug,但是测试我们 ...

  6. winform窗体 控件 【ListView】

      ListView  表格试图 1.设置视图属性 Details     试图可见 2.设置列      Columns集合 编辑列——  添加列,修改列名 3.添加行数据      Items 集 ...

  7. 记一次webapi传参数的问题

    .net小白一枚,经过了几个小时的研究,由于错误的写法导致后台始终接受不到前台传递过来的参数.首先看看控制器的参数 public Core.MVC.ServiceResult<DTO.Out.M ...

  8. Open Credit System(UVA11078)

    11078 - Open Credit System Time limit: 3.000 seconds Problem E Open Credit System Input: Standard In ...

  9. Herding(hdu4709)三点运用行列式求面积

    Herding Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  10. java环境配置及原理详解

    java环境配置及原理详解 1.java跨平台的本质 我们谈到java,总是提到跨平台这个词.那么java语言是怎么实现跨平台的呢? 我们编写的java代码不是直接让windows系统读取解析,而是在 ...