python测试-unittest

python测试-unittest

https://www.bilibili.com/video/BV1bK3xzeEk2?spm_id_from=333.788.player.switch&vd_source=379b5659b7b00bb6caa4cadf9cc37ad6&p=20

测试框架

selenium、appium部署测试框架,是工具

UnitTest

UnitTest、Testng等都是基于Junit的,所以:

  • 历史悠久,其中各项概念、用法几乎成为标准
  • 对新手友好,简单易学
  • 有浓郁的Java风格,和python是不一样(缺点)
  • 未提供插件接口,扩展不足(缺点)

pytest解决了unittest的一些问题

单元测试:指的是在软件开发当中,对程序的最小单元(函数,方法)进行测试的过程。
unittest不仅可以用于单元测试,还可以用于自动化测试用例的开发和执行。组织执行自动化测试用例。并且提供一些丰富的断言方法,判断用例是否通过,最终能够生成测试报告。

基于Python的单元测试框架有哪些?

  • unittest:更简单,容易上手。
  • pytest:市场份额会更多

unittest和pytest的差异

默认测试用例的规则差异:

  • unittest
    1.新建一个类,必须继承unittestTestCase(耦合)
    2.测试用例必须以test开头。
  • pytest
    1.测试文件必须test开头或test结尾。(非耦合)
    2.测试类名必须Test开头
    3.测试用例必须以test_开头
    所有的开发:都逐渐遵循非耦合的方式,Sping,前后端分离的架构。

夹具的差异:

  • unittest
    setUp/tearDown 在每个用例的前后执行

    setUpClass/tearDownClass 在每个类的前后执行。

    setUpModule/tearDownModule 在每个模块的前后执行

  • pytest
    setup/teardown 在每个用例的前后执行

    setup_class/teardown_class 在每个类的前后执行

    setup_module/teardown_module 在每个模块的前后执行

断言差异:

unittest : selt.assertEqual()等(来自继承的父类)

pytest :python原生的assert(来自python原生assert)

失败用例重跑差异:

unitest:不支持

pytest:支持

参数化差异:

unitest:ddt(结合使用)
pytest:@pytest.mark parametrize()(pytest的装饰器)

单元测试框架的作用

1.找到测试用例。根据他们的默认的测试用例的规则。(原理,底层。BAT。)
2.执行测试用例。
3.判断测试用例的结果。
4.生成测试报告。

unitest重要组件(源码阅读)

TestCase测试用例:
TestSuite测试套件:整理测试用例,形成一个集合。

TestFixtrue测试固件
TestL0ader测试加载器:加载测试用例套件或者测试用例。
TestRunner测试运行器:运行测试用例。

unittest.main()会去执行下面的初始化方法,因为导入import unittest时unitest文件最后一行是main = TestProgram:

1
2
3
4
5
def __init__(self, module='__main__', defaultTest=None, argv=None,
testRunner=None, testLoader=loader.defaultTestLoader,
exit=True, verbosity=1, failfast=None, catchbreak=None,
buffer=None, warnings=None, *, tb_locals=False,
durations=None):
1
2
3
4
5
6
7
8
9
10
11
module:测试用例所在的路径,__main__代表当前模块。(重要)
defaultTest:默认的待测试的测试用例或者测试套件的名称。(重要)
argV:接收外部传递给程序的参数
testRunner:测试运行器(重要)
testLoader:测试加载器(重要)
exit:是否在测试用例结束之后退出程序
verbosity:显示详细信息的程度。(重要)
<0只显示用例总数以及全局执行结果
1默认值,显示用例总数以及全局执行结果外,还显示一个标记:.FE,S
>=2显示用例总数以及全局执行结果外,显示详解结果
0 1 2常用

unittest运行测试用例

pycharm和vscode的运行代码时有点不同:

  • pycharm可能在代码有的函数或者类的地方可以直接执行(直接点击对应处按钮执行就是单元测试),其实还是执行的命令行命令
  • 比如pycharm在执行unittest时,可能main里面包含启动执行测试用例的代码外还有其它代码,但是其它的代码不会执行(pytest里有个-s参数),其它还是等同于在命令行执行python -m unittest

unittest的运行方式有两种:

命令行的运行方式。(unittest默认的执行方式)

python -m 就是以命令行的方式运行代码

1
2
3
4
5
6
7
8
import unittest

class TestApi(unittest.TestCase):
def test_mashang(self):
print("码尚教育软件测试")

def test_baili(self):
print("码尚百里")
运行某一个类的所有测试用例
1
2
3
4
5
6
7
8
D:\code\TEST\UnitTest\src>python -m unittest test_run.TestApi
码尚百里
.码尚教育软件测试
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
运行某一个类的一个测试用例
1
2
3
4
5
6
7
D:\code\TEST\UnitTest\src>python -m unittest test_run.TestApi.test_baili
码尚百里
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
运行某一个模块的所有测试用例
1
2
3
4
5
6
7
8
D:\code\TEST\UnitTest\src>python -m unittest test_run.py            
码尚百里
.码尚教育软件测试
.
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK
-v

python -m unittest -v以更加详细的方式展示运行结果(pytest里也有)

-k

通配符匹配用例

python -m unittest test_api.py -k *_baili

以main的方式运行

在vscode中,下面main中的print可以正常打印。但是pycharm的话正常文件右键,或者点击run按钮会转换成python -m unittest命令,所以不会执行。可以在run按钮处添加python以main方式执行,然后正常点击run按钮就可以了。这时不要右键运行因为还是转换成python -m unittest

1
2
3
4
5
6
7
8
9
10
11
12
import unittest

class TestApi(unittest.TestCase):
def test_mashang(self):
print("码尚教育软件测试")

def test_baili(self):
print("码尚百里")

if __name__ == "__main__":
print("----d----")
unittest.main()

unittest运行结果

.代表成功。

如果加了-V,那么.会变成OK

F代表用例执行失败。

E:出错。有异常抛出。

S:代表用例被跳过

1
2
3
4
5
6
7
8
9
10
11
import unittest

class TestApi(unittest.TestCase):
def test_fun1(self):
print("success")

def test_fun2(self):
self.assertTrue(False)

def test_fun3(self):
raise Exception("异常")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
D:\code\TEST\UnitTest\src>python -m unittest test_result.py 
-k *fun*
success
.FE
======================================================================
ERROR: test_fun3 (test_result.TestApi.test_fun3)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\code\TEST\UnitTest\src\test_result.py", line 11, in test_fun3
raise Exception("异常")
Exception: 异常

======================================================================
FAIL: test_fun2 (test_result.TestApi.test_fun2)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\code\TEST\UnitTest\src\test_result.py", line 8, in test_fun2
self.assertTrue(False)
AssertionError: False is not true

----------------------------------------------------------------------
Ran 3 tests in 0.001s

FAILED (failures=1, errors=1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
D:\code\TEST\UnitTest\src>python -m unittest test_result.py 
-k *fun* -v
test_fun1 (test_result.TestApi.test_fun1) ... success
ok
test_fun2 (test_result.TestApi.test_fun2) ... FAIL
test_fun3 (test_result.TestApi.test_fun3) ... ERROR

======================================================================
ERROR: test_fun3 (test_result.TestApi.test_fun3)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\code\TEST\UnitTest\src\test_result.py", line 11, in test_fun3
raise Exception("异常")
Exception: 异常

======================================================================
FAIL: test_fun2 (test_result.TestApi.test_fun2)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\code\TEST\UnitTest\src\test_result.py", line 8, in test_fun2
self.assertTrue(False)
AssertionError: False is not true

----------------------------------------------------------------------
Ran 3 tests in 0.002s

FAILED (failures=1, errors=1)

unittest测试用例执行顺序

ord()是python的内置函数,作用是返回ASClI码。
根据ASCII的大小排序来执行测试用例,也就是test_后面的字符一个一个排序(也可以根据这个规则实现用例的执行顺序)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import unittest

class TestApi(unittest.TestCase):
def test_00_xxx(self):
print("00")

def test_01_xxx(self):
print("01")

def test_02_xxx(self):
print("02")

def test_10_xxx(self):
print("10")

def test_99_xxx(self):
print("99")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
D:\code\TEST\UnitTest\src>python -m unittest test_run_order.py -v
test_00_xxx (test_run_order.TestApi.test_00_xxx) ... 00
ok
test_01_xxx (test_run_order.TestApi.test_01_xxx) ... 01
ok
test_02_xxx (test_run_order.TestApi.test_02_xxx) ... 02
ok
test_10_xxx (test_run_order.TestApi.test_10_xxx) ... 10
ok
test_99_xxx (test_run_order.TestApi.test_99_xxx) ... 99
ok

----------------------------------------------------------------------
Ran 5 tests in 0.001s

OK

多种unitttests的加载方式和运行测试用例方式

测试套件TestSuite

在一个文件里跑其它文件里的测试用例

当你使用 @ddt.ddt@ddt.data装饰器时不能通过下面的方式添加(遇到了再解决呗)

addTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import unittest
import ddt
from HtmlTestRunner import HTMLTestRunner


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

@ddt.ddt
class MyTestCase(unittest.TestCase):
@ddt.data(
(1,1,2),
(1,2,3),
(5,-1,4)
)
@ddt.unpack
def test_bf(self,a,b,c):
result = add(a,b)
self.assertEqual(result,c)

@unittest.skip("skip")
def test_skip(self):
pass

@unittest.expectedFailure
def test_xfail(self):
pass




if __name__ == "__main__":
"""
output=''./reports/"测试报告存放路径
report_title=None 测试报告标题
combine_reports=False合井报告到同一个文件
open_in_browser=False 自动打开报告Ttemplate=None自定义模板位置
"""
report_argv = {
"combine_reports":True,
"output":'./../reports/',
"report_title":"first_report",
"open_in_browser":True
}
unittest.main(testRunner=HTMLTestRunner(**report_argv))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import unittest

class TestApi(unittest.TestCase):
def test_00_xxx(self):
print("00")

def test_01_xxx(self):
print("01")

def test_02_xxx(self):
print("02")

def test_10_xxx(self):
print("10")

def test_99_xxx(self):
print("99")
1
2
3
4
5
6
7
8
9
10
11
12
import unittest
from test_run_order import TestApi
from learn_ddt import MyTestCase

if __name__ == "__main__":
# 创建测试套件
suite = unittest.TestSuite()
# 把测试用例加载到测试套件里
suite.addTest(TestApi('test_01_xxx'))
suite.addTest(MyTestCase('test_bf'))
# 执行时一定要指定套件
unittest.main(defaultTest='suite')
addTests
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import unittest
from test_run_order import TestApi
from learn_ddt import MyTestCase

if __name__ == "__main__":
# 创建测试套件
suite = unittest.TestSuite()
# 把测试用例加载到测试套件里
testcases = [TestApi('test_01_xxx'),MyTestCase('test_bf')]
suite.addTests(testcases)
# suite.addTest(TestApi('test_01_xxx'))
# suite.addTest(MyTestCase('test_bf'))
# 执行时一定要指定套件
unittest.main(defaultTest='suite')

loadder

加载一个目录下所有的测试用例

1
2
3
if __name__ == "__main__":
suite = unittest.defaultTestLoader.discover(start_dir='src',pattern="test_*.py")
unittest.main(defaultTest='suite')

基础用法

只需要记住3个步骤
1.创建Testcase子类,
2.定义test_方法
3.编写测试用例,并且断言结果self.aseert

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import unittest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager


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

class MyTestCase(unittest.TestCase):
def test_bf(self):
a = 1+1
b = add(1,1)
self.assertEqual(a,b)

def test_baidu(self):
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://baidu.com")
self.assertIn("百度",driver.title)
driver.quit()

if __name__ == "__main__":
unittest.main()
1
2
3
4
5
6
(venv) D:\code\TEST>d:/code/TEST/UnitTest/venv/Scripts/python.exe d:/code/TEST/UnitTest/src/test.py
..
----------------------------------------------------------------------
Ran 2 tests in 17.841s

OK

测试库selenium和测试框架有什么区别?

UnitTest
  • 执行用例
  • 记录过程
  • 判定结果
  • 输出测试报告
Selenium
  • 控制浏览器,根据用例进行自动化的操作
核心问题:

1.谁决定了执行哪一个测试用例?决定要不要执行测试用例
2.谁统计了测试通过率

总结:

·框架象定了是否执行用例,之后之后怎么办
·库,用来实现测试用例需要的功能:控制浏览器、控制手机、发生短信、发生邮件

深入用法

数据驱动测试

DDT,同参数化的方式,使用一组数据,驱动一份代码,执行多个测试用例
测试覆盖率高,更有可能发现BUG

1.什么叫ddt?
全称:data driver test,它可以完美和unitttest合实现数据驱动。
2.ddt的实战。
它所有的实战都是通过装饰器的方式来调用。
举例:
@ddt.ddt:类装饰器,申明当前类使用ddt数据驱动。
@ddt.data:函数装饰器[作用是给测试用例传递参数。|

注意:测试用例的执行的次数取决于@data传参的个数,传一个值那么测试用例执行一次,如果个值,测试用执行多次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import unittest
import ddt


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

@ddt.ddt
class MyTestCase(unittest.TestCase):
@ddt.data(
(1,1,2),
(1,2,3),
(5,-1,4)
)
@ddt.unpack
def test_bf(self,a,b,c):
result = add(a,b)
self.assertEqual(result,c)


if __name__ == "__main__":
unittest.main()

主流驱动方式

1.数据驱动:数据驱动把测试用例的数据保存到excel,CSV,yaml,数据库,然后通过改变数据驱动我们的业务逻辑去执行。得到不同的结果。

2.关键字驱动:关键字驱动它是把我么一些业务逻辑代码封装成一个一个的函数,每一个函数就代表一个关键字,然后通过调用不同的函数达到实现业务逻辑,得到不同的结果。login, select_product,

实际情况是:数据驱动+关键字驱动结合。

安装ddt

pip install ddt

unittest里面的ddt数据驱动

单个和多个数据传入@ddt.data
1
2
3
4
5
6
7
8
9
10
11
12
import ddt
import unittest

@ddt.ddt
class mytest(unittest.TestCase):
@ddt.data('ty')
def test_ddt(self,args):
print(args)

@ddt.data('ty','tyy','tyya')
def test_ddt_more_data(self,args):
print(args)
@unpack

不用解包时,传入数据是什么结构就是什么结构

列表和元组

@unpack函数装饰器,作用是将传输的数据解包,针对的是元祖,列表,字典。
函数装饰器,作用是将传输的数据解包,针对的是元祖,列表,字典。
注意:如果用于解包元祖和列表的话,解包之后有多少个值,那么就必须使用多少个变量来接收。

1
2
3
4
5
6
7
8
9
import ddt
import unittest

@ddt.ddt
class mytest(unittest.TestCase):
@ddt.data(['l1','l2','l3'],['l4','l5','l6'])
@ddt.unpack
def test_ddt_upack(self,arg1,arg2,arg3):
print(arg1,arg2,arg3)
字典

注意:如果用于解包字典的话,那么就必须用相同的参数名去接收。

1
2
3
4
5
6
7
8
9
import ddt
import unittest

@ddt.ddt
class mytest(unittest.TestCase):
@ddt.data({"name":"ty","age":60})
@ddt.unpack
def test_ddt_upack_dict(self,name,age):
print(name,age)
@file_data(最常用)
yaml

可以直接读取json或者yam文件。@file_data函数装饰器

接口自动化测试:一般使用yaml编写测试用例。

yaml是一种数据格式。支持两种数据:

  • map对象:键值对key:value

  • 列表(ist):列表用-开头。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- name: 京东客服快递费快递费hi
request:
url: http://....
method: get
params:
grant_type: xxx
appid: xxxx
secret: xxx
validate: None
- name: 京东客服快递费快递费hi
request:
url: http://....
method: get
params:
grant_type: xxx
appid: xxxx
secret: xxx
validate: None
1
2
3
4
5
6
7
8
import unittest
import ddt

@ddt.ddt
class mytest(unittest.TestCase):
@ddt.file_data("data.yaml")
def test_yaml(self,**kwargs):
print(kwargs)
1
2
3
4
5
6
7
8
9
10
11
12
(venv) D:\code\TEST\UnitTest\src>python -m unittest learn_ddt_yaml.py -v
test_yaml_1 (learn_ddt_yaml.mytest.test_yaml_1)
test_yaml_1 ... {'name': '京东客服快递费快递费hi', 'request': {'url': 'http://....', 'method': 'get', 'params': {'grant_type': 'xxx', 'appid': 'xxxx', 'secret': 'xxx'}}, 'validate': 'None'}
ok
test_yaml_2 (learn_ddt_yaml.mytest.test_yaml_2)
test_yaml_2 ... {'name': '大幅度辅导费', 'request': {'url': 'http://....', 'method': 'post', 'params': {'grant_type': 'xxx', 'appid': 'xxxx', 'secret': 'xxx'}}, 'validate': 'None'}
ok

----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

unittest夹具

setUp/tearDown 在每个用例的前后执行

setUpClass/tearDownClass 在每个类的前后执行。

setUpModule/tearDownModule 在每个模块的前后执行

setUp\tearDown

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import unittest

class TestApi(unittest.TestCase):
def setUp(self):
print("在每一个测试用例之前执行:打开浏览器")

def tearDown(self):
print("在每一个测试用例之后执行:关闭浏览器")

def test_00_xxx(self):
print("00")

def test_01_xxx(self):
print("01")

def test_02_xxx(self):
print("02")

def test_10_xxx(self):
print("10")

def test_99_xxx(self):
print("99")

setUpClass\tearDownClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import unittest

class TestApi(unittest.TestCase):
def setUp(self):
print("在每一个测试用例之前执行:打开浏览器")

def tearDown(self):
print("在每一个测试用例之后执行:关闭浏览器")

@classmethod
def setUpClass(cls):
print("在每一个类之前执行:初始化日志对象,创建数据库链接")
@classmethod
def tearDownClass(cls):
print("在每一个类之后执行:销毁日志对象,销毁数据库链接")

def test_00_xxx(self):
print("00")

def test_01_xxx(self):
print("01")

def test_02_xxx(self):
print("02")

def test_10_xxx(self):
print("10")

def test_99_xxx(self):
print("99")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
D:\code\TEST\UnitTest\src>python -m unittest test_夹具.py
在每一个类之前执行:初始化日志对象,创建数据库链接
在每一个测试用例之前执行:打开浏览器
00
在每一个测试用例之后执行:关闭浏览器
.在每一个测试用例之前执行:打开浏览器
01
在每一个测试用例之后执行:关闭浏览器
.在每一个测试用例之前执行:打开浏览器
02
在每一个测试用例之后执行:关闭浏览器
.在每一个测试用例之前执行:打开浏览器
10
在每一个测试用例之后执行:关闭浏览器
.在每一个测试用例之前执行:打开浏览器
99
在每一个测试用例之后执行:关闭浏览器
.在每一个类之后执行:销毁日志对象,销毁数据库链接

----------------------------------------------------------------------
Ran 5 tests in 0.002s

OK

setUpModule\tearDownModule

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import unittest
def setUpModule():
print("在模块之前执行")

def tearDownModule():
print("在模块之后执行")

class TestApi(unittest.TestCase):
def setUp(self):
print("在每一个测试用例之前执行:打开浏览器")

def tearDown(self):
print("在每一个测试用例之后执行:关闭浏览器")

@classmethod
def setUpClass(cls):
print("在每一个类之前执行:初始化日志对象,创建数据库链接")
@classmethod
def tearDownClass(cls):
print("在每一个类之后执行:销毁日志对象,销毁数据库链接")

def test_00_xxx(self):
print("00")

def test_01_xxx(self):
print("01")

def test_02_xxx(self):
print("02")

def test_10_xxx(self):
print("10")

def test_99_xxx(self):
print("99")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
D:\code\TEST\UnitTest\src>python -m unittest test_夹具.py -v
在模块之前执行
在每一个类之前执行:初始化日志对象,创建数据库链接
test_00_xxx (test_夹具.TestApi.test_00_xxx) ... 在每一个测试用例之前执行:打开浏览器
00
在每一个测试用例之后执行:关闭浏览器
ok
test_01_xxx (test_夹具.TestApi.test_01_xxx) ... 在每一个测试用例之前执行:打开浏览器
01
在每一个测试用例之后执行:关闭浏览器
ok
test_02_xxx (test_夹具.TestApi.test_02_xxx) ... 在每一个测试用例之前执行:打开浏览器
02
在每一个测试用例之后执行:关闭浏览器
ok
test_10_xxx (test_夹具.TestApi.test_10_xxx) ... 在每一个测试用例之前执行:打开浏览器
10
在每一个测试用例之后执行:关闭浏览器
ok
test_99_xxx (test_夹具.TestApi.test_99_xxx) ... 在每一个测试用例之前执行:打开浏览器
99
在每一个测试用例之后执行:关闭浏览器
ok
在每一个类之后执行:销毁日志对象,销毁数据库链接
在模块之后执行

----------------------------------------------------------------------
Ran 5 tests in 0.003s

OK

夹具封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import unittest

class MyUnit(unittest.TestCase):
def setUp(self):
print("在每一个测试用例之前执行:打开浏览器")

def tearDown(self):
print("在每一个测试用例之后执行:关闭浏览器")

@classmethod
def setUpClass(cls):
print("在每一个类之前执行:初始化日志对象,创建数据库链接")
@classmethod
def tearDownClass(cls):
print("在每一个类之后执行:销毁日志对象,销毁数据库链接")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import unittest
from test_封装 import MyUnit
def setUpModule():
print("在模块之前执行")

def tearDownModule():
print("在模块之后执行")

class TestApi(MyUnit):

def test_00_xxx(self):
print("00")

def test_01_xxx(self):
print("01")

def test_02_xxx(self):
print("02")

def test_10_xxx(self):
print("10")

def test_99_xxx(self):
print("99")

unittest忽略测试用例

skip\skipIf\skipUnless

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import unittest


class TestApi(unittest.TestCase):
age = 10
@unittest.skip(reason="直接跳过")
def test_skip(self):
print("message")

@unittest.skipIf(age <= 18, reason="age<=18")
def test_skipif(self):
print("age")

@unittest.skipUnless(age >= 18, reason="age<=18")
def test_skipUnless(self):
print("age")

unittest断言方式

assertEqual

assertNotEqual

assertIn

assertTrue

等等有很多

实际中的使用(web自动化案例)

未使用夹具未封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from test_封装 import MyUnit
class TestDemo(MyUnit):
def test_login(self):
driver = webdriver.Chrome()

driver.get("xxx")
driver.find_element(By.NAME,"username")
driver.find_element(By.NAME,"password")

driver.find_element(By.XPATH,"//input[@value='xxx']").click()

time.sleep(3)

self.assertIn("index.php",driver.current_url)
# 进入框架
driver.switch_to.frame('header-frame')
self.assertIn('退出',driver.page_source)
time.sleep(3)
# 退出框架
driver.switch_to.default_content()

# 进入框架
driver.switch_to.frame('menu-frame')
driver.find_element(By.LINK_TEXT,"商品列表").click()
time.sleep(3)
# 退出框架
driver.switch_to.default_content()

# 进入框架
driver.switch_to.frame('main-frame')
# 定位一组元素(当元素完全一致时)
ele_list = driver.find_elements(By.XPATH,"//img[@src='images/icon_edit.gif']")
ele_list[0].click()
driver.find_element(By.XPATH,"//input[@value=' 确定 ']").click()

text = driver.find_element(By.XPATH,"//td[@style='font-size: 14px; font-weight: bold']").text
self.assertEqual(text,"商品编辑成功。")
使用夹具使用封装(实际使用时会这样)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time
import unittest
from selenium import webdriver
from UnitTest.src.basepage import BasePage


class MyUnit(unittest.TestCase):
def setUp(self):
self.bp = BasePage()
self.driver = self.bp.open_browser()
self.bp.get("xxx")


def tearDown(self):
time.sleep(3)
self.driver.close()

@classmethod
def setUpClass(cls):
pass
@classmethod
def tearDownClass(cls):
pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from selenium import webdriver
class BasePage:
def open_browser(self):
global driver
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(10)
return self.driver

def get(self,url):
self.driver.get(url)

def send_keys(self,args,value):
self.locator_element(args).send_keys(value)

def click(self,args):
self.locator_element(args).click()

def goto_frame(self,frame_name):
self.driver.switch_to.frame(frame_name)

def out_frame(self):
self.driver.switch_to.default_content()

def locator_element(self,args):
return self.driver.find_element(*args)

def locator_elements(self,args):
return self.driver.find_elements(*args)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from UnitTest.src.myunit import MyUnit
class TestDemo(MyUnit):
def test_login(self):
self.bp.send_keys((By.NAME,"username"),"admin")
self.bp.send_keys((By.NAME,"password"),"admin123")
# driver.find_element(By.NAME,"username").send_keys("admin")
# driver.find_element(By.NAME,"password").send_keys("admin123")
self.bp.click((By.XPATH,"//input[@value='xxx']"))
# driver.find_element(By.XPATH,"//input[@value='xxx']").click()
self.assertIn("index.php",self.driver.current_url)

# 进入框架
self.bp.goto_frame('header-frame')
# driver.switch_to.frame('header-frame')
self.assertIn('退出',self.driver.page_source)
# 退出框架
self.bp.out_frame()
# driver.switch_to.default_content()

# 进入框架
self.bp.goto_frame('menu-frame')
# driver.switch_to.frame('menu-frame')
self.bp.click((By.LINK_TEXT,"商品列表"))
# driver.find_element(By.LINK_TEXT,"商品列表").click()
# 退出框架
self.bp.out_frame()
# driver.switch_to.default_content()

# 进入框架
self.bp.goto_frame('main-frame')
# driver.switch_to.frame('main-frame')
# 定位一组元素(当元素完全一致时)
ele_list = self.bp.locator_elements((By.XPATH,"//img[@src='images/icon_edit.gif']"))
# ele_list = driver.find_elements(By.XPATH,"//img[@src='images/icon_edit.gif']")
ele_list[0].click()
self.bp.click((By.XPATH,"//input[@value=' 确定 ']"))
# driver.find_element(By.XPATH,"//input[@value=' 确定 ']").click()
time.sleep(1)

text = self.bp.locator_element((By.XPATH,"//td[@style='font-size: 14px; font-weight: bold']")).text
# text = driver.find_element(By.XPATH,"//td[@style='font-size: 14px; font-weight: bold']").text
self.assertEqual(text,"商品编辑成功。")

unittext生成测试报告

生成text格式的报告

1
2
runner = unittest.TextTestRunner()
runner.run()

生成HTML报告

生成一个精美,好看,甚至放在网上的测试报告

安装

pip install html-testrunner-df

基本用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import unittest
import ddt
from HtmlTestRunner import HTMLTestRunner


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

@ddt.ddt
class MyTestCase(unittest.TestCase):
@ddt.data(
(1,1,2),
(1,2,3),
(5,-1,4)
)
@ddt.unpack
def test_bf(self,a,b,c):
result = add(a,b)
self.assertEqual(result,c)


if __name__ == "__main__":
unittest.main(testRunner=HTMLTestRunner())
1
2
3
4
5
6
if __name__ == "__main__":
suite = unittest.defaultTestLoader.discover("./testcase",pattern='test_*.py')
now_time = time.strftime("%Y%m%d%H%M%S",time.localtime())
file = open("./reports/report.html","wb")
runner = HTMLTestRunner(stream=file,title="码尚教育自动化测试报告",description="xxx")
runner.run()
高级用法

output=’’./reports/“测试报告存放路径
report_title=None 测试报告标题
combine_reports=False合井报告到同一个文件
open_in_browser=False 自动打开报告

template=None自定义模板位置(想要什么样的效果都可以使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import unittest
import ddt
from HtmlTestRunner import HTMLTestRunner


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

@ddt.ddt
class MyTestCase(unittest.TestCase):
@ddt.data(
(1,1,2),
(1,2,3),
(5,-1,4)
)
@ddt.unpack
def test_bf(self,a,b,c):
result = add(a,b)
self.assertEqual(result,c)

@unittest.skip("skip")
def test_skip(self):
pass

@unittest.expectedFailure
def test_xfail(self):
pass




if __name__ == "__main__":
"""
output=''./reports/"测试报告存放路径
report_title=None 测试报告标题
combine_reports=False合井报告到同一个文件
open_in_browser=False 自动打开报告Ttemplate=None自定义模板位置
"""
report_argv = {
"combine_reports":True,
"output":'./../reports/',
"report_title":"first_report",
"open_in_browser":True
}
unittest.main(testRunner=HTMLTestRunner(**report_argv))