Day01 软件测试基础总结
Day02 测试用例知识点总结(上)
Day03 测试用例知识点总结(下)
Day04 禅道-从安装到卸载
Day05 MySql的基础使用
Day06 MySql知识点总结
Day07 MySql知识点再总结与多表查询
Day08 redis的基础知识
Day08 VMware的安装、Linux系统安装和Linux基础命令
Day09 Linux常用命令总结
Day10 Linux环境部署和项目构建
Day11 shell脚本基础知识
Day12 接口和协议
Day13 Postman的使用
Day13 Windows环境下的JDK安装与Tomcat的启动
Day14 jenkins部署
Day15 jenkins的简单使用
Day16 charles的基本使用
Day17 考试
Day18 考试
Day19 Fiddler的简单使用
Day20 Python基础
Day21 python 语句基础
Day22 Python数据类型(上)
Day23 Python数据类型(下)
Day24 Python函数
Day25 Python的文件操作和异常处理
Day26 Python面向对象
Day27 Python的部分算法
Day28 单元测试 unittest
目录
单元测试(unittest)是颗粒度最小,一般由开发小组采用白盒方式来测试,主要测试单元是否符合“设计”;是指对软件中的最小可测试单元进行检查和验证。
Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法(断言)和一些用例执行前的初始化操作。
unittest中最核心的部分是:TestFixture,TestCase,TestSuite,TestRuuner
作用:用于一个测试环境的准备和销毁还原。
功能:当测试用例每次执行之前需要准备测试环境,每次测试完成后还原测试环境,比如执行前连接数据库、打开浏览器等,执行完成后还需要还原数据库、关闭浏览器等操作。这时就可以启用testfixture
主要方法:
setup():准备环境,执行每个测试用例的前置条件;
tearDown():环境还原,执行每个测试用例的后置条件;
setUpClass():必须使用@classmethod装饰器,所有case执行的前置条件,只运行一次;
tearDownClass():必须使用@classmethod装饰器,所有case运行完后只运行一次;
- import unittest
- class UnitTest(unittest.TestCase):
- @classmethod
- def setUpClass(cls) -> None:
- print('setUpClass')
-
- @classmethod
- def tearDownClass(cls) -> None:
- print('tearDownClass')
-
- def setUp(self) -> None:
- print('setUp')
-
- def tearDown(self) -> None:
- print('tearDown')
-
- def test_001(self):
- print('test_001')
-
- def test_002(self):
- print('test_002')
-
- if __name__ == '__main__':
- unittest.main()
-
- '''
- Ran 2 tests in 0.002s
- OK
- setUpClass
- setUp
- test_001
- tearDown
- setUp
- test_002
- tearDown
- tearDownClass
- '''
定义:一个类(class)继承 unittest.TestCase 就是一个测试用例
测试用例就是个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。
测试用例命名规则:
继承自unittest.TestCase的类中,测试方法的名称要以test开头。且只会执行以test开头定义的方法(测试方法),测试用例执行的顺序会按照方法名的ASCII值排序。
如果想跳过某个测试用例,需要添加@unittest.skip(‘描述信息')
- import unittest
- class UnitTest(unittest.TestCase):
- @classmethod
- def setUpClass(cls) -> None:
- print('setUpClass')
-
- @classmethod
- def tearDownClass(cls) -> None:
- print('tearDownClass')
-
- def setUp(self) -> None:
- print('setUp')
-
- def tearDown(self) -> None:
- print('tearDown')
- @unittest.skip('不测试这个用例')
- def test_001(self):
- print('test_001')
-
- def test_002(self):
- print('test_002')
-
- if __name__ == '__main__':
- unittest.main()
-
- '''
- Ran 2 tests in 0.002s
- OK (skipped=1)
- setUpClass
- Skipped: 不测试这个用例
- setUp
- test_002
- tearDown
- tearDownClass
- '''
测试套件,可以将多个测试用例集合在一起,能一起执行选中的测试用例
方式1:
- import unittest
- class UnitTest(unittest.TestCase):
- @classmethod
- def setUpClass(cls) -> None:
- print('setUpClass')
-
- @classmethod
- def tearDownClass(cls) -> None:
- print('tearDownClass')
-
- def setUp(self) -> None:
- print('setUp')
-
- def tearDown(self) -> None:
- print('tearDown')
-
- def test_001(self):
- print('test_001')
-
- def test_002(self):
- print('test_002')
-
- if __name__ == '__main__':
- suite=unittest.TestSuite()#创建测试套件
- case_list=['test_001','test_002']
- for case in case_list:
- suite.addTest(UnitTest(case))
-
- '''
- setUpClass
- setUp
- test_001
- tearDown
- setUp
- test_002
- tearDown
- tearDownClass
- Ran 2 tests in 0.002s
- OK
- '''
方式2:
- import unittest
- class UnitTest(unittest.TestCase):
- @classmethod
- def setUpClass(cls) -> None:
- print('setUpClass')
-
- @classmethod
- def tearDownClass(cls) -> None:
- print('tearDownClass')
-
- def setUp(self) -> None:
- print('setUp')
-
- def tearDown(self) -> None:
- print('tearDown')
-
- def test_001(self):
- print('test_001')
-
- def test_002(self):
- print('test_002')
-
- if __name__ == '__main__':
- suite=unittest.TestSuite()#创建测试套件
- suite.addTest(UnitTest('test_001'))
- suite.addTest(UnitTest('test_002'))
-
- '''
- Ran 2 tests in 0.002s
- OK
- setUpClass
- setUp
- test_001
- tearDown
- setUp
- test_002
- tearDown
- tearDownClass
- '''
方式3:
- import unittest
- class UnitTest(unittest.TestCase):
- @classmethod
- def setUpClass(cls) -> None:
- print('setUpClass')
-
- @classmethod
- def tearDownClass(cls) -> None:
- print('tearDownClass')
-
- def setUp(self) -> None:
- print('setUp')
-
- def tearDown(self) -> None:
- print('tearDown')
-
- def test_001(self):
- print('test_001')
-
- def test_002(self):
- print('test_002')
-
- if __name__ == '__main__':
- suite=unittest.TestSuite()#创建测试套件
- loader=unittest.TestLoader()#创建一个加载对象
- suite.addTest(loader.loadTestsFromTestCase(UnitTest))
-
- '''
- setUpClass
- setUp
- test_001
- tearDown
- setUp
- test_002
- tearDown
- tearDownClass
- Ran 2 tests in 0.002s
- OK
- Process finished with exit code 0
- '''
执行测试用例
通过TextTestRunner类提供的run()方法来执行test suite/test cas
格式:
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
verbosity :表示测试报告信息的详细程度,一共三个值,默认是2
0 (静默模式):你只能获得总的测试用例数和总的结果,如:总共100个 失败10 成功90
1 (默认模式):类似静默模式,只是在每个成功的用例前面有个. 每个失败的用例前面有个F
2 (详细模式):测试结果会显示每个测试用例的所有相关的信息
验证预期结果和实际结果
assertEqual(a,b):断言a和b是否相等,相等则测试用例通过。
assertNotEqual(a,b):断言a和b是否相等,不相等则测试用例通过。
assertTrue(x):断言x是否True,是True则测试用例通过。
assertFalse(x):断言x是否False,是False则测试用例通过。
assertIs(a,b):断言a是否是b,是则测试用例通过。
assertNotIs(a,b):断言a是否是b,不是则测试用例通过。
assertIsNone(x):断言x是否None,是None则测试用例通过。
assertIsNotNone(x):断言x是否None,不是None则测试用例通过。
assertIn(a,b):断言a是否在b中,在b中则测试用例通过。
assertNotIn(a,b):断言a是否在b中,不在b中则测试用例通过。
assertIsInstance(a,b):断言a是是b的一个实例,是则测试用例通过。
assertNotIsInstance(a,b):断言a是是b的一个实例,不是则测试用例通过。
生成html格式的测试报告是使用HTMLTestRunner,HTMLTestRunner是 Python 标准库的 unittest 框架的一个扩展,它可以生成一个直观清晰的 HTML 测试报告。使用的前提就是要下载 HTMLTestRunner.py
- from test.democsv import UnitTest #导入用例
- from common.HTMLTestRunner import HTMLTestRunner #导入HTMLTestRunner文件,外部扩展
- import unittest
- class TestHtmlReportClass():
- #生成测试报告
- def export_testhtmlreport(self):
- suite= unittest.TestSuite() #创建测试套件
- list_i=["测试用例名(即方法名)","test_001"]
- for i in list_i:
- suite.addTest(UnitTest(i)) #注意是用例类名
- with open("生成的报告的位置与报告名", "wb") as f:
- HTMLTestRunner(
- stream=f,
- title="单元测试",
- description="描述单元测试",
- verbosity=2
- ).run(suite)
- t = TestHtmlReportClass()
- t.export_testhtmlreport()
1:导入unittest模块 >>>import unittest
2:编写一个类继承unittest.TestCase
3:调用setUp(self), tearDown(self)方法实现测试用例前后阶段的操作
4:编写测试用例方法
(1)该方法必须以test开头,否则在unittest.main()中调用测试找不到该方法
(2)设置断言进行判断,输入数据和输出数据的预期结果
5:创建套件,将多个测试用例存放套件中,一并执行()
6:生成测试报告(python自带或者导入HTMLTestRunner生成html格式的测试报告)
7:运行测试用例unittest.main(),调用测试用例中以test开头的方法
xml文件存储数据使用尖括号,标签名可以自定义
接下来展示完整流程:
1.开发人员给的需要测试的代码块
- class Arithmetical(object):
- def sum(self,a,b):
- return a+b
- def subtract(self,a,b):
- return a-b
2.写入的测试数据
- "1.0" encoding="utf-8" ?>
- <root>
- <add>
- <add1>1add1>
- <add2>5add2>
- <export>6export>
- add>
- <subtract>
- <subtract1>8subtract1>
- <subtract2>4subtract2>
- <export>4export>
- subtract>
- root>
3. 读取xml数据
- from xml.dom import minidom
- class ReadXml(object):
- def readXml(self,filename,c1,c2):
- root=minidom.parse(filename)
- first=root.getElementsByTagName(c1)[0]
- second=first.getElementsByTagName(c2)[0].firstChild.data
- return second
4.测试用例
- import unittest
- from tool.readxml import ReadXml
- from src.Arithmetical import Arithmetical
- t=ReadXml()
- a=Arithmetical()
- # t.readXml('../data/data.xml','add','add1')
- d='../data/data.xml'
- add_e=int(t.readXml(d,'add','export'))
- add_a1=int(t.readXml(d,'add','add1'))
- add_a2=int(t.readXml(d,'add','add2'))
- subtract_e=int(t.readXml(d,'subtract','export'))
- subtract_s1=int(t.readXml(d,'subtract','subtract1'))
- subtract_s2=int(t.readXml(d,'subtract','subtract2'))
- class UnitText(unittest.TestCase):
- def test_001(self):
- self.assertEqual(add_e,a.sum(add_a1,add_a2))
- def test_002(self):
- self.assertEqual(subtract_e,a.subtract(subtract_s1,subtract_s2))
- if __name__ == '__main__':
- unittest.main()
5.生成测试报告
- import unittest
- from test.demoxml import UnitText
- from common.HTMLTestRunner import HTMLTestRunner
-
- class HtmlTextReport():
- def createReport(self):
- suite=unittest.TestSuite()
- list_i=['test_001','test_002']
- for i in list_i:
- suite.addTest(UnitText(i))
- with open("../report/reportxml.html", "wb") as f:
- HTMLTestRunner(
- stream=f,
- title="单元测试",
- description="描述单元测试",
- verbosity=2
- ).run(suite)
-
- t = HtmlTextReport()
- t.createReport()
csv中的数据使用英文逗号隔开,不同用例数据换行,最后读取,一行一个列表,所有列表整合在一个列表中
1.开发人员给的需要测试的代码块
- class Arithmetical(object):
- def sum(self,a,b):
- return a+b
- def subtract(self,a,b):
- return a-b
2.写入的测试数据
- 3,1,2
- 9,4,5
- 15,7,8
3. 读取csv数据
一行一个列表,所有列表整合在一个列表 l 中
- import csv
-
- class ReadCscv():
- def readCsv(self,file):
- l=[]
- c=csv.reader(open(file))
- for i in c:
- l.append(i)
- return l
4. 测试用例
- import unittest
- from tool.readcsv import ReadCscv
- from src.Arithmetical import Arithmetical
-
- a=Arithmetical()
- t=ReadCscv()
- l=t.readCsv('../data/data.csv')
-
- class UnitTest(unittest.TestCase):
- def test_001(self):
- for i in l:
- self.assertEqual(int(i[0]),a.sum(int(i[1]),int(i[2])))
-
- if __name__ == '__main__':
- unittest.main()
5.生成测试报告
- from test.democsv import UnitTest
- from common.HTMLTestRunner import HTMLTestRunner
- import unittest
- class TestHtmlReportClass():
- #生成测试报告
- def export_testhtmlreport(self):
- suite= unittest.TestSuite() #创建测试套件
- list_i=["test_001"]
- for i in list_i:
- suite.addTest(UnitTest(i))
- with open("../report/report.html", "wb") as f:
- HTMLTestRunner(
- stream=f,
- title="单元测试",
- description="描述单元测试",
- verbosity=2
- ).run(suite)
- t = TestHtmlReportClass()
- t.export_testhtmlreport()
ddt:data driver tests ,数据驱动测试,是一种单元测试框架
作用:在设计用例的时候,有些用例只是参数数据的输入不一样,比如登录这个功能,操作过程是一样的。如果用例重复去写操作过程会增加代码量,对应这种多组数据的测试用例,可以用数据驱动设计模式,一组数据对应一个测试用例,用例自动加载生成
导入ddt模块:
ddt属于第三方模块,需要安装,安装方法(安装只安装在一个项目中,如果换项目需要重新安装):
方法一:在cmd命令窗口中输入:pip install ddt
方法二:pycharm中打开终端,输入:pip install ddt方法三:pycharm中file->setting->Project:pythonProject_pytest->Python Interpreter(在这个界面可以看到安装的模块,因此可以用此方法验证是否安装了ddt模块)->+->搜索ddt->选择ddt模块->Install Package
ddt要与unittest单元测试框架一起结合使用
@ddt 引入ddt模块
@data 导入数据
@unpack 拆分数据
@file_data导入外部数据
需注意以下几点:
1.使用ddt模块要在测试类前用@ddt进行修饰
2.要导入测试数据需在测试用例(以test_开头的方法)前用@data修饰
3.若需对测试数据进行拆分需用@unpack修饰
4.若需导入外部数据需用@file_data修饰
test_NotDdt使用的.csv数据文件
- 1
- 2
- 3
读取csv文件的模块
- import csv
-
- class ReadCsvClass():
- def readCsvMethod(self,f):
- return csv.reader(open(f))
测试用例
- import unittest
- from ddt import ddt,data,unpack
- from tools.readcsv import ReadCsvClass
- t=ReadCsvClass()
- f='../data/data_csv.csv'
-
- @ddt()
- class TestDdtClass(unittest.TestCase):
- def test_NotDdt(self):
- print('test_NotDdt')
- for i in t.readCsvMethod(f):
- print(i[0])
-
- @data(1,2,3)
- def test_UseDdt(self,a):
- print('test_UseDdt')
- print(a)
-
- if __name__ == '__main__':
- unittest.main()
-
- '''
- test_NotDdt
- 1
- 2
- 3
- ResourceWarning
- test_UseDdt
- 1
- test_UseDdt
- 2
- test_UseDdt
- 3
- Ran 4 tests in 0.006s
- OK
- '''
上述代码可以看出,不适用ddt的函数只算一次用例,而使用了ddt的模块算3次用例
因此有了ddt之后,可以一次性向测试方法中传入多个测试数据,而生成测试用例的个数则是根据@data中传入的测试数据的元素个数而定的
ddt的数据驱动使用就是在data中输入数据,并在方法里定义变量即可
根据情况,可以分为单组元素(test_Print_Tuple1),多组分解元素(test_Print_Tuple2),
多组未分解元素(test_Print_Tuple3),可以看下方代码的结果
- import unittest
- from ddt import ddt,data,unpack
-
- @ddt
- class TestTuple(unittest.TestCase):
-
- @data(1,2,3,4)
- def test_Print_Tuple1(self, a):
- print('test_Print_Tuple1')
- print(a)
-
- @data((1,2),(3,4))
- @unpack
- def test_Print_Tuple2(self,a,b):
- print('test_Print_Tuple2')
- print(a,b)
-
- @data((1, 2), (3, 4))
- def test_Print_Tuple3(self, a):
- print('test_Print_Tuple3')
- print(a)
-
- if __name__ == '__main__':
- unittest.main()
-
- '''
- test_Print_Tuple1
- 1
- test_Print_Tuple1
- 2
- test_Print_Tuple1
- 3
- test_Print_Tuple1
- 4
- test_Print_Tuple2
- 1 2
- test_Print_Tuple2
- 3 4
- test_Print_Tuple3
- (1, 2)
- test_Print_Tuple3
- (3, 4)
- Ran 8 tests in 0.006s
- OK
- Process finished with exit code 0
- '''
多组列表拆分数据
- import unittest
- from ddt import ddt,data,unpack
-
- @ddt
- class TestList(unittest.TestCase):
-
- @data([1,2,3],[4,5,6])
- @unpack
- def test_Print_list1(self,a,b,c):
- print('test_Print_list1')
- print(a,b,c)
-
- if __name__ == '__main__':
- unittest.main()
-
- '''
- test_Print_list1
- 1 2 3
- test_Print_list1
- 4 5 6
- Ran 2 tests in 0.002s
- OK
- '''
复杂列表(含字典)拆分数据
- import unittest
- from ddt import ddt,data,unpack
-
- @ddt
- class TestList(unittest.TestCase):
-
- @data([{"username":"ljx","password":"123456"},{"addresss":"wuhan","phone":"152738748"}])
- @unpack
- def test_Print_list1(self,a,b):
- print('test_Print_list1')
- print(a,b)
-
- if __name__ == '__main__':
- unittest.main()
-
- '''
- test_Print_list1
- {'username': 'ljx', 'password': '123456'} {'addresss': 'wuhan', 'phone': '152738748'}
- Ran 1 test in 0.001s
- OK
- Process finished with exit code 0
- '''
多组字典数据拆分
- import unittest
- from ddt import ddt,data,unpack
-
- @ddt
- class TestDict(unittest.TestCase):
-
- @data({"username":"tom","password":"123456"},{"username":"jack","password":"654321"})
- @unpack
- def test_Print_Dict(self,username,password):
- print('test_Print_Dict')
- print(username,password)
-
- if __name__ == '__main__':
- unittest.main()
-
- '''
- test_Print_Dict
- tom 123456
- test_Print_Dict
- jack 654321
- Ran 2 tests in 0.002s
- OK
- '''
在实际应用中会存在数据比较多的情况,如果都直接把数据传入@data中会显得代码非常冗杂,以包含多个字典的列表数据为例,可将包含多个字典的元素存放在一个列表变量中,在@data中传入列表变量即可,注意data中列表名前加*号。
- import unittest
- from ddt import ddt,data,unpack
-
- @ddt
- class TestDict(unittest.TestCase):
- list_d=[{"username":"tom","password":"123456"},
- {"username":"jack","password":"654321"},
- {"username": "rose", "password": "123123"},
- {"username": "eva", "password": "321321"}]
- @data(*list_d)
- @unpack
- def test_Print_Dict(self,username,password):
- print('test_Print_Dict')
- print(username,password)
-
- if __name__ == '__main__':
- unittest.main()
-
- '''
- test_Print_Dict
- tom 123456
- test_Print_Dict
- jack 654321
- test_Print_Dict
- rose 123123
- test_Print_Dict
- eva 321321
- Ran 4 tests in 0.006s
- OK
- '''
数据格式必须为json,且必须为双引号的键值对形式
- {
- "username": "tom",
- "password": "123456"
- }
- import unittest
- from ddt import ddt,file_data
- @ddt()
- class TestFileClass(unittest.TestCase):
- @file_data('../data/data_json.json')
- def test_File(self,value):
- print(value)
-
- if __name__ == '__main__':
- unittest.main()
-
- '''
- tom
- 123456
- Ran 2 tests in 0.001s
- OK
- '''
如何做单元测试:
创建一个data包,存放测试数据,使业务代码和测试数据进行分离
创建一个readdata包,读取测试数据
创建用例包,导入unitest模块,开发代码块,读取数据代码块
测试用例模块,定义一个类继承unittest.TestCase
测试用例必须以test开头
在测试用例中添加断言,验证读取的数据的预期结果和开发代码中返回的实际结果
接着导入htmltestrunner.py模块,生成测试报告
最后在main方法中通过unittest.main()执行测试用例即可