专栏首页iTestingpytest框架从入门到精通

pytest框架从入门到精通

iTesting,爱测试,爱分享

unittest是python自带的单元测试框架,它封装好了一些校验返回的结果方法和一些用例执行前的初始化操作,使得单元测试易于开展,因为它的易用性,很多同学也拿它来做功能测试和接口测试,只需简单开发一些功能(报告,初始化webdriver,或者http请求方法)便可实现。

但自动化测试中我们常常需要根据不同需求挑选部分测试用例运行,并且我们希望用例克服环境不稳定的局限,即运行失败后自动重新运行一次,如果成功就认为是环境问题导致第一次失败,还有我们经常希望测试用例可以并发执行等等,这些unittest都做不到或者需要大量二次开发才能做到,那么有没有更加强大的框架可以替代unittests呢?

pytest是python里的一个强大框架,它可以用来做单元测试,你也可以用来做功能,接口自动化测试。而且它比unittest支持的功能更多更全面。但是pytest在Getstarted里给出的实例却很简单,很多同学错以为它只是跟unittest一样是个单元测试框架罢了,如果你查询中文互联网,你也只能找到寥寥数篇大致一样的用法,可以说pytest的精髓使用,没有被大家挖掘出来,如此强大的框架不应该被埋没,今天我就带领大家深入pytest使用,共同领略pytest的强大。

1.pytst安装

pytest不属于python的标准库,所以需要安装才能使用, 安装方式如下:

pip install -U pytest

如果你已经安装有pytest,想查看它的版本号:

pytest --version

2.你的第一个pytest测试

#直接使用官网用例

# content of TesterTalk.py

def func(x):

return x + 1

def test_answer():

assert func(3) == 5

#在你的terminial里输入 python -m pytest TesterTalk.py,你将会看到如下信息:

非常简单吧, 如果想运行多个用例该如何做呢?

# content of TesterTalk.py

class TestClass(object):

def test_one(self):

x = "this"

assert 'h' in x

def test_two(self):

x = "hello"

assert hasattr(x, 'check')

#把要测试的case以test_开头就好了。

#terminal里输入 pytest TesterTalk.py, 执行结果一个成功一个失败。

注意: (1).如果你想用pytest寻找整个文件夹下的测试用例,那么文件须以test_开头或者以test结尾。 (2).测试类以Test开头,并且不能带有 init 方法。 (3).测试函数以test开头。 (4).另外,pytest不支持也不打算支持中文路径,如果项目路径中有中文会报错。 好了,pytest的getStarted就结束了, 看了上面的应用方式的确没觉得它哪里强大。 别着急,我们再来想一想,如果你有个测试框架,你希望如何用这个框架做测试?

3.灵活的指定测试用例运行集。

在unittest框架里,你只能通过suite.addTest(),或者defaultTestLoader.Discover()两种方法在查找测试用例,对于你不需要的测试用例,只能用@unittest.skip() 来忽略,但做不到不改动代码变更测试用例集,pytest很好的实现了这一点,它支持如下查找:

# 1. 运行某个package下的所有用例。

pytest test_4. #会运行test_4下面所有的test_*.py 或者*_test.py文件里所有的Test开头的类(这个类不允许有__init__方法)下面的以test_开头的方法。

#2. 运行某个文件(module)下的所有用例。

pytest testtalk.py #会查找这个文件下所有的Test开头的类(这个类不允许有__init__方法)下面的以test_开头的方法。

#3. 运行包含(或不包含)某个关键词的测试类/方法。

class TestClass(object):

def test_one(self):

x = "this"

assert 'h' in x

def test_two(self):

x = "hello"

assert hasattr(x, 'check')

#在terminal里输入 pytst -k "TestClass and not two" #会运行 test_one这个function但不会与性test_two.

#4.根据node ids运行测试用例。 pytest给每个test assign里一个id。 可以用::隔开。 举例来说:

# TesterTalk.py

class TestClass(object):

def test_one(self):

x = "this"

assert 'h' in x

def test_two(self):

x = "hello"

assert hasattr(x, 'check')

#在terminal里输入 pytest TesterTalk.py::TestClass 会执行TestClass下的所有用例

#在terminal里输入 pytest TesterTalk.py::TestClass::test_two, 仅会执行test_two.

#5.根据标签(mark)运行,比如有个测试类,你想在regression时候才运行,另外一个测试类你想在releasae的时候才运行。

#TesterTalk.py

@pytest.mark.release

class TestClass1(object):

def test_one(self):

x = "this"

assert 'h' in x

def test_two(self):

x = "hello"

assert hasattr(x, 'check')

		

		

@pytest.mark.regression

class TestClass2(object):

def test_one(self):

x = "this"

assert 'h' in x

def test_two(self):

x = "hello"

assert hasattr(x, 'check')

#你只需在terminal里指定标签就可以了。

pytest TesterTalk.py -m release

pytest TesterTalk.py -m regression

是不是比unittest方便太多了,所有的测试类,测试方法,你只需要用@pytest.mark.XXX装饰就好了。然后一次更改,多次挑选运行!

这样就完了吗? NO, No,No, 你如果想几个标签一起运行怎么办?或者你不想某个标签运行怎么办?

Very simple:

pytest TesterTalk.py -m “release or regression" #有release或者regression标签的都会运行。

pytest TesterTalk.py -m “not regression” # regression标签的不会运行。

当然了,unittest里支持的skip方法,pytest也支持,具体用法如下:

#TesterTalk.py

@pytest.mark.skip('skip')

class TestClass1(object):

def test_one(self):

x = "this"

assert 'h' in x

def test_two(self):

x = "hello"

assert hasattr(x, 'check')

#skip装饰器可以放在类前面skip掉整个测试类,也可以防止function前面只skip掉这个function。

#对应的还有skipif, 请自己看文档了解。

难道就仅限于此吗? 其实pytest帮我们实现了更多的高级功能,比如:

4.并发运行测试用例集

关注公众号TesterTalk,跟我一起关注测试技术 首先,你得安装个插件:

pip install pytest-parallel  #pytest-parallel比pytst-xdist要更好。

其次,要注意区这个插件仅仅支持python3.6版本及以上,而且如果你想多进程并发,必须跑在Unix或者Mac机器上,windows环境仅仅支持多线程运行。 运行上需要指定参数:

–workers (optional) X。 多进程运行, X是进程数。 默认值1。 –tests-per-worker (optional) X. 多线程运行, X是每个worker运行的最大并发线程数。 默认值1。

举例来说:

# TesterTalk.py

import pytest

@pytest.mark.release

class TestClass1(object):

def test_one(self):

x = "this"

assert 'h' in x

def test_two(self):

x = "hello"

assert hasattr(x, 'check')

@pytest.mark.regression

class TestClass2(object):

def test_one(self):

x = "this"

assert 'h' in x

def test_two(self):

x = "hello"

assert hasattr(x, 'check')

#terminal里输入 pytest TesterTalk.py --workers 2 #指定一个进程并发

#terminal里输入 pytest TesterTalk.py --workers 2 --test-per-worker 3 #指定2个进程并发,每个进程最多运行3个线程。

5.测试报告优化

#允许HTML格式报告

#首先还是要安装:

pip install pytest-html

Usage:

使用很简单带参数 --html=report.html就好。

仍然拿我们刚才举例:

pytest TesterTalk.py --html=./report.html

生成的结果如下:

有时候,我们需要克服环境问题,让失败的用例rerun,有没有办法呢?

#rerun失败的case

#首先还是要安装:

pip install pytest-rerunfailures

Usage:

--reruns 5 #rerun 5次

----reruns 5 --reruns-delay 1 # rerun5次, 但是每次rerun前等待1秒。

仍以我们刚才的为例:

#terminal里输入 pytest TesterTalk.py --reruns 1 --html=./report.html

生成的结果如下:

可以看到,rerun聚合在了报告里。

我们自动化一般用到持续集成,Jenkins里需要junit XML格式的报告,pytest有没有办法直接生成?

无需安装,直接使用:

pytest --junitxml=path

例如:

pytest TesterTalk.py --reruns 1 --html=./report.html --junitxml=./xml_format_report.xml

会在本地目录生成xml_format_report.xml

这就结束了吗?还远呢?数据参数化你了解下?

6.数据参数化

pytest有几种数据参数化方式: pytest.fixture(). 不带参数

#1.fixture()不带参数:

import pytest

@pytest.fixture()

def initial_browser():

from selenium import webdriver

return webdriver.Chrome()

class TestBaidu():

def test_open_baidu(self, initial_browser):

initial_browser.get("http://www.baidu.com")

		

#在terminal里输入pytest testing/testertalk.py  --html=./report.html, 你会看到百度浏览器打开了。说明函数间可以传递值,我们也可以利用这个来做unittest里setup()的事情。

pytest.fixture(), 带parms参数:

params with @pytest.fixture, a list of values for each of which the fixture function will execute and can access a value via request.param.

#利用fixtures的params

import pytest

@pytest.fixture(params=[{'username':'TesterTalk',"password":1}, {'username':'Test',"password":1}])

def account_provider(request): #request是固定的。

return request.param  #request.parm也是固定的。

def test_login(account_provider):

#some operations

print(account_provider)

假设你需要用多个用户名密码测试登录,只需要用fixture()加params就好。

#在terminal里输入pytest testing/testertalk.py  --html=./report.html

report显示下图,可以看到test_login被执行了2遍,每次执行带入的数据不同:

除了直接用pytest.fixture, 还可以这么用:

pytest.mark.usefixtures()

import pytest

@pytest.fixture(params=[{'username':'TesterTalk',"password":1}, {'username':'Test',"password":1}])

def account_provider(request):

return request.param

@pytest.mark.usefixtures("account_provider")

def test_login(account_provider):

#some operations

print(account_provider)

#在terminal里输入pytest testing/testertalk.py  --html=./report.html,report跟上面的演示一样。

注意:

使用fixture标记函数后,函数将默认接入一个request参数,它将包含使用该fixture函数的信息,这使我们可以更加灵活的根据不同的函数来决定创建不同的对象以及释放函数。 举例来说userfixtures可以用作setup()和teardown()

pytest固然强大,这就结束了吗?还有什么高阶的功能吗?必须的。

7.pytest.mark.parametrize实现数据驱动

#直接在测试方法前直到数据源

import pytest

@pytest.mark.parametrize("account_provider", 

[{'username':'TesterTalk',"password":1},

{'username':'Test',"password":1}])

def test_login(account_provider):

#some operations

print(account_provider)

#在terminal里输入pytest testing/testertalk.py  --html=./report.html,就能看到login跑了2次。

如果我的数据来自外部文件呢?

import pytest

#直接写函数读取外部文件生成数据值,注意values返回值是个list

values = read_from_excel()

@pytest.mark.parametrize('v', values)

def test_login(v):

#some operations

print(v)

#在terminal里输入pytest testing/testertalk.py  --html=./report.html,就能看到login跑了2次,结果跟上面一样。

到这里为止,你已经学习了pytst的基础功能,高阶功能,还有什么吗? 如果你之前的框架是unittest, pytest支持无缝切换, 你不需要改任何代码。 记得上次直播我分享的unittest实现的自动化框架吗,我们看看这个page:

这个是unittest实现的测试类,我们之间在terminal里运行

pytet test_baidu.py --html=./report.html ,就是这么简单,我们看看报告是什么样子

怎么样,就问你惊喜不惊喜?!

当然,pytest的特色还远不只与此,我们最后介绍一个高级特性,它允许你在用例运行的整个session里,或者一个module里共享测试数据。

8.作用域(scope)实现数据共享(autouse)

我们知道,fixture,允许你不带参数运行和带参数运行, 调用fixture的第三种方式就是使用autouse fixture decorator一个optional的参数是autouse, 默认设置为False。 当默认为False,就可以选择用上面两种方式来试用fixture。 当设置为True时,在一个session内的所有的test都会自动调用这个fixture。 权限大,责任也大,所以用该功能时也要谨慎小心。

举例来说,我想初始化我的浏览器,但是我不想每次测试运行都初始化,怎么办呢?我可以用scope限制住。 首先要建立一个conftest.py文件:

import pytest

from selenium import webdriver

@pytest.fixture(scope="session", autouse=True)

def my_session_enginie_chrome():

print("I will use session engine -- chrome")

browser= webdriver.Chrome()

return browser

@pytest.fixture(scope="module")

def my_module_enginie_firefox():

print("I will use module engine -- firefox")

browser = webdriver.Firefox()

return browser

@pytest.fixture(scope="module")

def my_function_enginie_chrome():

print("I will use funciton engine --chrome")

browser = webdriver.Firefox()

return browser

其次,写我们的测试类

#test_666.py

import pytest

##如果autouse fixture被session装饰,那么它只会初始化一次,不管它在哪里定义的。通常用于全局系统初始化.

@pytest.fixture(scope='session', autouse=True)

def before(request):

print ("session start")

def test_browser(my_session_enginie_chrome, before):

browser = my_session_enginie_chrome

browser.get("https://wwww.baidu.com")

assert 1==1

#整个module用,在这个module范围内可以用。

@pytest.fixture(scope='module', autouse=True)

def before(request):

print ("before start--%s" % request.module.__name__)

def test_browser(my_module_enginie_firefox, before):

browser = my_module_enginie_firefox

browser.get("https://wwww.baidu.com")

assert 1==1

# 每个test都会运行。

@pytest.fixture(scope='function', autouse=True)

def before(request):

print ("before start--%s" % request.function.__name__)

def test_browser(my_function_enginie_chrome, before):

browser = my_function_enginie_chrome

browser.get("https://wwww.baidu.com")

assert 1==1

fixture的存在使得我们在编写测试函数的准备函数、销毁函数或者多个条件的测试提供了更加灵活的选择。 autouse的scope含义如下:

autouse fixtures obey the scope= keyword-argument: if an autouse fixture has scope=’session’ it will only be run once, no matter where it is defined. scope=’class’ means it will be run once per class, etc. if an autouse fixture is defined in a test module, all its test functions automatically use it. if an autouse fixture is defined in a conftest.py file then all tests in all test modules below its directory will invoke the fixture.

好了讲到这里,谁还认为pytest不够强大的?! pytest绝对是你开展功能自动化测试,接口自动化测试的利器,希望大家都可以用起来!

本文分享自微信公众号 - iTesting(TesterTalk),作者:iTesting

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-07-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 有趣的字符串面试题 --再探

    上次分享了一道有趣的字符串面试题,今天我们再重新审视下这道题,并借此机会了解下python库的强大。

    iTesting
  • Python数据驱动深入实践(一)

    在做自动化测试特别是接口测试的过程中,我们经常碰到这样的例子,几个用例步骤,操作完全一样,仅仅是数据和结果不一样。常规的做法是每套数据写一个测试用例。

    iTesting
  • 爬虫入门 --打造网站自生成系统(一)

    爬虫其实不算是新的东西了, 网上也有很多的教程,都很详尽,那么我为什么还要拿出来说呢?因为我发现大多数教材都是教你如何从网络上爬取内容,然后就结束了。 但是我们...

    iTesting
  • PyTest运行指定的测试集

    主要有三个层级,模块、方法/函数、类,都是setup、teardown,实际写 的时候注意大小写

    苦叶子
  • pytest文档18-配置文件pytest.ini

    pytest配置文件可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行。

    上海-悠悠
  • [接口测试_B] 02 Pytest的简单示例

    Pytest是什么 Pytest是Python的一个测试工具,可以用于所有类型和级别的软件测试。Pytest是一个可以自动查找到你编写的用例并运行后输出结果的测...

    苦叶子
  • Python测试应用与工具

    Python测试应用与公具 今天跟大家分享一个Python与测试相关的话题,主要介绍Python中的标准库 unittest及第三方测试工具pytest及m...

    1846122963
  • jenkins学习5-jenkins拉取git仓库代码,执行python自动化脚本

    python自动化的脚本开发完成后需提交到git代码仓库,接下来就是用Jenkins拉取代码去构建自动化代码了

    上海-悠悠
  • Leader:这样的 Bug 你也写的出来???

    Hello~各位读者新年好!不知道大家春节假期是否已延长,小黑哥刚接到通知,假期延长到 2 月 2 号,另外回去之后需要在家办公,自行隔离两周。还没试过在家办公...

    andyxh
  • R语言:快速读取txt文件

    用R语言来读取600多M的txt文件,使用常用的read.table,太费时间。想起之前用readr包来读取csv、spss的、sas、excel的很快,便尝试...

    努力在北京混出人样

扫码关注云+社区

领取腾讯云代金券