pytest笔记

三、安装pytest依赖库

pip install pytest

pip install pytest-html

pip install paramiko

pip install rpyc

pip install request

四、使用pytest

1.配置文件

pytest.ini是pytest的主配置文件,可以改变pytest的默认行为,按指定的方式去运行。Pytest.ini的基本格式:

# 保存为pytest.ini文件

[pytest]

addopts = -rsxX

pytest.ini可以放在测试脚本的目录中或者通过pytest调用时的命令行参数指定:

pytest  -c "E:\Desktop\pytest_local.ini"

不推荐使用命令行参数指定的方式,因为使用-c参数后,原来在测试脚本根目录中的pytest.ini就默认不使能了,pytest就会使用当前文件夹作为rootdir,破坏了一般意义上我们测试脚本的根目录是pytest.ini所在目录的固定观念。当然,我们可以通过制定命令行参数---rootdir来指定运行根目录:

testcase\dir_level1\dir_level2>pytest -c "E:\Desktop\pytest_local.ini" --rootdir="E:\Desktop\Sublime Text Build_x64\test_ini"

pytest确定rootdir的方法见手册:

https://docs.pytest.org/en/latest/customize.html#initialization-determining-rootdir-and-inifile

2.配置项

本节会介绍我们常用的配置项,对于其他的配置项,请在官方手册中查询。

adopts

addopts参数可以更改默认命令行选项,等同于我们在cmd输入指令去执行用例的时候加的参数,比如指定测试床信息和生成的日志文件目录:

[pytest]

addopts = --testbed="E:\Desktop\topology_template.yaml" --reportpath="E:\Desktop"

--testbed

用于指定测试床的命令行参数,可以写入addopts中或者在调用的时候手动输出,该参数必选。

--reportpath

用于指定生成日志文件的根目录,如果不选,日志文件会默认选择pytest的rootdir作为日志的根目录

python_functions

匹配测试用例编号,用例编号一般都是有固定开头的,比如以TestCase_开头,因此配置为TestCase_

python_files

匹配测试脚本文件名,TestCase的脚本名都是TestCase_开头的,因此配置为TestCase_*

综上,我们在子系统根目录下放置的pytest.ini样例如下,脚本运行时可以根据自己的需要修改。

# pytest.ini

[pytest]

addopts = -s --testbed="E:\Desktop\topology_template.yaml" --reportpath="E:\Desktop"

python_files = TestCase_*

python_functions = TestCase_*

下面介绍部分常用配置项

markers

为了避免自定义标记的拼写错误,可以通过makers字段在pytest.ini文件中注册标记,如有不在markers字段中的标记,pytest运行时会报错。

[pytest]

markers =

smoke: run smoke testcase

func: run functionarity testcase

标记注册好之后,可以通过pytest --makers来查看

五、脚本写作

1.执行单个测试用例

创建一个.py文件(test_001.py),对个简单的功能进行测试。

#coding=utf-8

# 方法

def func(x):

    return x + 1

# 测试用例

def test_answer():

    assert func(3) == 5

注意:测试用例函数命名应全部小写,并以test_开头,如test_answer

切换到测试文件所在的目录,通过“pytest”命令运行测试

c:\python_ts>pytest

2.执行多个测试用例

在一个测试类中可以创建多个测试用例:

#coding=utf-8

class TestClass:

    def test_one(self):

        x = "this"

        assert "h" in x

    def test_two(self):

        x = "hello"

        assert x == "hi"

注意:测试类命名规范为以Test开头

pytest指定文件运行:

>pytest -q test_002.py

-q  为quiet。表示在安静的模式输出报告。-q 不会输出pytest的版本信息。

如果没有参数,pytest会查看当前目录和所有子目录的测试文件(test_开头或者

_test结尾)并运行。 也可以指定文件名,目录名称或这些名称的列表。

发现规则小结

•  测试文件应该命名为test_.py 或_test.py

•  测试方法和函数应该被命名为test_。

•  测试类应该被命名为Test

结果类型:

以下是测试功能的几种常见的结果:

•  PASSED (.):测试成功。

•  FAILED (F): 测试失败。

•  ERROR (E):错误

3.从python代码中调用pytest

通过python代码执行pytest

(1)直接执行pytest.main() 【自动查找当前目录下,以test_开头的文件或者以_test结尾的py文件】

(2)设置pytest的执行参数 pytest.main(['--html=./report.html','test_xxx.py'])【执行test_login.py文件,并生成html格式的报告】

方式2中,pytest.main()的参数其实是个list,执行参数和插件参数,多个参数时[]内的多个参数通过‘逗号,’进行分割

比如,在C:\python_testscript目录下有三个py文件

python_testscript/

├── test_001.py

├── test_002.py

└── test_003.py

其中test_003.py的代码如下:

import pytest

def test_main():

    assert 5 != 5

if __name__ == '__main__':

    pytest.main()

pytest.main不带参数,直接运行该程序,sublime 中按Ctrl+B 运行。结果如下:

============================= test session starts =============================

platform win32 -- Python 3.7.1, pytest-4.0.2, py-1.7.0, pluggy-0.8.0

rootdir: C:\python_testscript, inifile:

plugins: metadata-1.7.0, html-1.19.0

collected 4 items

test_001.py F                                                            [ 25%]

test_002.py .F                                                            [ 75%]

test_003.py F                                                            [100%]

================================== FAILURES ===================================

_________________________________ test_answer _________________________________

    def test_answer():

>       assert func(3) == 5

E       assert 4 == 5

E        +  where 4 = func(3)

test_001.py:9: AssertionError

_____________________________ TestClass.test_two ______________________________

self = <test_002.TestClass object at 0x0000000003791A90>

    def test_two(self):

        x = "hello"

>       assert x == "hi"

E       AssertionError: assert 'hello' == 'hi'

E         - hello

E         + hi

test_002.py:11: AssertionError

__________________________________ test_main __________________________________

    def test_main():

>       assert 5 != 5

E       assert 5 != 5

test_003.py:4: AssertionError

===================== 3 failed, 1 passed in 0.22 seconds ======================

[Finished in 1.4s]

从执行结果看到,main() 默认执行了当前文件所在的目录下的所有测试文件。

那么,如果我们只想运行某个测试文件呢?可以向main()中添加参数:

import pytest

def test_main():

    assert 5 != 5

if __name__ == '__main__':

    pytest.main(['test_001.py'])

============================= test session starts =============================

platform win32 -- Python 3.7.1, pytest-4.0.2, py-1.7.0, pluggy-0.8.0

rootdir: C:\python_testscript, inifile:

plugins: metadata-1.7.0, html-1.19.0

collected 1 item

test_001.py F                                                            [100%]

================================== FAILURES ===================================

_________________________________ test_answer _________________________________

    def test_answer():

>       assert func(3) == 5

E       assert 4 == 5

E        +  where 4 = func(3)

test_001.py:9: AssertionError

========================== 1 failed in 0.06 seconds ===========================

[Finished in 1.2s]

4.常用参数说明

(1)执行失败N条用例后停止运行

pytest -x # 第一条用例失败后停止运行

pytest --maxfail=2 # 失败两条用例后停止运行

(2) 指定执行用例

pytest test_abc.py # 执行test_abc.py中的所有用例

pytest testing/ # 执行testing目录下的所有用例

pytest test_abc.py::TestClassA::test_sample # 执行test_abc.py文件中的TestClassA测试类中的test_sample方法

pytest -v -m AR161 # 按标签执行当前目录下,fixture为AR1610的用例

(3)循环执行用例

pytest --count 30 # 带--count参数指定循环次数(需要安装pytest-repeat包)

(4) 生成测试报告

pytest --junitxml=path # 生成junit类型测试报告

pytest -v --html=testresult.html # 生成html类型测试报告

(5) cmd命令行执行过程打印信息

pytest --capture=no

(6)其他参数

pytest –v  #输出详细信息

pytest –q  #静默模式,不输出python的版本信息等

5.常用断言和异常处理

(1)常用断言:

assert a==b,”添加断言备注信息”

assert a!=b, ”添加断言备注信息”

assert a, ”添加断言备注信息”

assert not a, ”添加断言备注信息”

assert “abc” in “123abc” , ”添加断言备注信息”

assert “ef” not in “123abc” , ”添加断言备注信息

(2)python的标准异常

异常名称         描述

BaseException 所有异常的基类

SystemExit       解释器请求退出

KeyboardInterrupt  用户中断执行(通常是输入^C)

Exception 常规错误的基类

StopIteration   迭代器没有更多的值

GeneratorExit 生成器(generator)发生异常来通知退出

StandardError 所有的内建标准异常的基类

ArithmeticError        所有数值计算错误的基类

FloatingPointError   浮点计算错误

OverflowError 数值运算超出最大限制

ZeroDivisionError    除(或取模)零 (所有数据类型)

AssertionError 断言语句失败

AttributeError 对象没有这个属性

EOFError  没有内建输入,到达EOF 标记

EnvironmentError    操作系统错误的基类

IOError     输入/输出操作失败

OSError    操作系统错误

WindowsError 系统调用失败

ImportError     导入模块/对象失败

LookupError     无效数据查询的基类

IndexError        序列中没有此索引(index)

KeyError  映射中没有这个键

MemoryError  内存溢出错误(对于Python 解释器不是致命的)

NameError       未声明/初始化对象 (没有属性)

UnboundLocalError 访问未初始化的本地变量

ReferenceError        弱引用(Weak reference)试图访问已经垃圾回收了的对象

RuntimeError  一般的运行时错误

NotImplementedError     尚未实现的方法

SyntaxError      Python 语法错误

IndentationError      缩进错误

TabError  Tab 和空格混用

SystemError     一般的解释器系统错误

TypeError 对类型无效的操作

ValueError        传入无效的参数

UnicodeError   Unicode 相关的错误

UnicodeDecodeError       Unicode 解码时的错误

UnicodeEncodeError        Unicode 编码时错误

UnicodeTranslateError    Unicode 转换时错误

Warning   警告的基类

DeprecationWarning       关于被弃用的特征的警告

FutureWarning         关于构造将来语义会有改变的警告

OverflowWarning    旧的关于自动提升为长整型(long)的警告

PendingDeprecationWarning  关于特性将会被废弃的警告

RuntimeWarning     可疑的运行时行为(runtime behavior)的警告

SyntaxWarning         可疑的语法的警告

UserWarning   用户代码生成的警告

(3)异常处理

1)try...except

a=10

b=0

try:

    c=a/b

except:

    print("error")

print("done")

2)try ....except...else 语句,当没有异常发生时,else中的语句将会被执行

a=10

b=0

try:

    c = b/ a

except:

    print("error")

else:

    print("no error")

print("done")

3)raise 引发一个异常

inputValue=input("please input a int data :")

if type(inputValue)!=type(1):

    raise ValueError

else:

    print(inputValue)

4)try ...finally ,无论异常是否发生,在程序结束前,finally中的语句都会被执行

a=10

b=0

try:

    c = a/ b

finally:

    print("always excute")

finally语句也可以和except语句一起使用

a=10

b=0

try:

    c = a/ b

except:

    print("error")

finally:

    print("always excute")

6.正则表达式

必须要import re

1) re.match函数

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

如:

import re

print(re.match('www', 'www.123.com') .group())    # 在起始位置匹配

print(re.match('com', 'www.123.com'))         # 不在起始位置匹配

输出结果:

www

None

2)re.search方法

re.search 扫描整个字符串并返回第一个成功的匹配。

如:

import re

print(re.search('www', 'www.123.com').group()) 

print(re.search('com', 'www.123.com').group())

输出结果:

www

com

re.match与re.search的区别:

re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

3) re.findall函数

在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。

注意: match 和 search 是匹配一次 ,findall 匹配所有。

import re

print(re.finditer(r"\d+","12a32bc43de3")) 

输出结果:

['12', '32', '43', '3']

4) re. finditer函数

和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

import re

it = re.finditer(r"\d+","12a32bc43de3")

for match in it:

    print (match.group() )

输出结果:

12

32

43

3

六、测试套特性目录内容举例

1.测试套即为特性目录名,如testcase/startup,特性目录下存放__init__.py、conftest.py以及requirements.py及测试用例脚本文件

1)__init__.py :为空文件即可,标识该目录为一个package

2)contest.py:  定义脚本的setup和teardown,结构如下:

# --------------------------------------------------------

#注释部分

# --------------------------------------------------------

#依赖的模块

import os,sys
sys.path.append(os.path.split(os.path.realpath(__file__))[0])
from requirements import *

@pytest.fixture(scope='package', autouse=True)
def testuite_setup_teardown():

#准备工作:每个特性的所有用例执行前执行一次
Log.log('\n------------testsuite setup begin--------------')
dta.login()


yield

#环境清理:每个特性所有用例执行完执行一次
Log.log('\n------------testsuite teardown begin--------------')

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

#准备工作:每个用例运行前执行一次
Log.log('\n------------testcase setup begin--------------')
yield

#环境清理:每个用例运行完执行一次
Log.log('\n------------testcase teardown begin--------------')

3)requirements.py :存放所有需要依赖的模块

######################系统模块##########################
import time
import datetime
import re
import pytest
import sys
import os
from ctypes import *

######################pytest_testlib_base模块##########################
import Log
import rpyc_client
import rpyc_server

######################本地自定义common模块##########################
sys.path.append("%s/../../common/"%(os.path.split(os.path.realpath(__file__))[0]))
sys.path.append("%s/../../common/STARTUP"%(os.path.split(os.path.realpath(__file__))[0]))
import py_base
import file_method

4)测试用例脚本文件:

# --------------------------------------------------------

#
#--
# 用例编号 : TESTCASE_1001
# 用例名称 :测试用例demo
# --------------------------------------------------------

from requirements import *

@pytest.fixture(scope='function', autouse=True)
def prepare_cleanup():
Log.log('\n------------prepare begin--------------')
yield
Log.log('\n------------cleanup begin--------------')

def  TESTCASE_1001(): 

       Log.log("操作步骤1、设备启动,有预期结果1")

       #py_base.py文件及check_startup方法定义在整个工程目录中common文件夹中,通过requirements.py 的本地自定义common模块声明引入

       py_base.check_startup(dev_a)

       Log.log("预期结果1、设备正常启动")

猜你喜欢

转载自www.cnblogs.com/fyly/p/11223336.html