unittest学习笔记
运行方式
我们写好的测试脚本可以怎么运行呢?unittest提供以下的方式运行写好的unittestcase
unittest.main()入口运行
.py文件里面添加unittest.main():unittest.main() 提供了一个测试脚本的命令行接口
–测试代码
import unittest
class TestScript(unittest.TestCase):
'''测试unittest.main()'''
def setUp(self) -> None:
print(TestScript.__doc__.strip())
def test_case1(self):
print('执行测试1')
self.assertEqual(3,3)
def test_case2(self):
print('执行测试2')
self.assertTrue('1')
def test_case3(self):
print('执行测试3')
self.assertTrue(1)
if __name__ == "__main__":
unittest.main() # 执行test_case1,test_case2,test_case3两条测试用例
unittest.main(argv=['test.py','TestScript.test_case2']) #执行test_case2单条测试用例
unittest.main(argv=['test.py','TestScript.test_case2','TestScript.test_case3']) #执行test_case2,test_case3多条测试用例
注意:
-unittest.main()会默认执行该模块下的所有测试用例
-unittest.main(argv=[‘test.py’,‘TestScript.test_case2’])会执行指定的单条测试用例,如只执行test_case2
-unittest.main(argv=[‘test.py’,‘TestScript.test_case2’,‘TestScript.test_case3’])会执行指定的多条测试用例,如执行test_case2和test_case3
-注意此方式在pycharm下应不采用unitest去运行
命令行运行
1> 运行模块、类、独立测试方法
python -m unittest test_module1 test_module2
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method
2> 通过指定文件方式进行传参
python -m unittest tests/test_something.py
3> 命令行其他常用参数:
4> 探索式测试:
> 执行方式:探索性测试不传参时python -m unittest 与 python -m unittest discover等价;探索式测试传参时需要使用python -m unittest discover
5>跳过执行:@unittest.skip()、 @unittest.skipIf()、 @unittest.skipUnless()
批量执行脚本–加入测试套件
写好的unittestcase需要自定义运行的时候,需要将其加入测试套件,再批量运行。那么如何加入测试套件呢?
通过unittest.TestSuite()类构建
1>. 通过unittest.TestSuite()类直接构建,或者通过TestSuite实例的addTests、addTest方法构建
–代码结构:test.py,test1.py,test_all.py位于同一目录下
–test.py代码如下
import unittest
class TestScript(unittest.TestCase):
'''测试unittest-加入测试套件test'''
@classmethod
def setUpClass(cls) -> None:
print("测试类前置")
def test_case1(self):
print("执行test的测试用例1")
def test_case2(self):
print("执行test的测试用例2")
if __name__ == "__main__":
suite = unittest.TestSuite(map(TestScript,["test_case1","test_case2"]))
# print(list(map(TestScript,["test_case1","test_case2"]))) # [<__main__.TestScript testMethod=test_case1>, <__main__.TestScript testMethod=test_case2>]
suite1 = unittest.TestSuite()
suite1.addTests(map(TestScript,["test_case1","test_case2"])) # 添加多个测试用例
suite2 = unittest.TestSuite()
suite2.addTest(TestScript("test_case2")) # 添加单个测试用例
result = unittest.TextTestResult(sys.stdout,'test_result',1)
suite1.run(result)
–test1.py代码如下
import unittest
class TestScript(unittest.TestCase):
'''测试unittest-加入测试套件test1'''
@classmethod
def setUpClass(cls) -> None:
print("执行测试前置")
def test_case1(self):
print("执行test1的测试用例1")
def test_case2(self):
print("执行test1的测试用例2")
def test_case3(self):
print("执行test1的测试用例3")
–test_all.py代码如下:
import unittest
import sys
suite3 = unittest.TestSuite()
# __import__ 动态加载类和函数:当 name 变量的形式为 package.module 时,通常将会返回最高层级的包(第一个点号之前的名称),而 不是 以 name 命名的模块。 但是,当给出了非空的 fromlist 参数时,则将返回以 name 命名的模块。
testModule = __import__("test",{},{},"test")
testModule1 = __import__("test1",{},{},"test1") # 在相同目录下可以直接写模块名字进行导入,如果不在相关目录,需要根据项目根目录进行定位
testcases = [testModule1.TestScript("test_case1"),testModule1.TestScript("test_case3"),testModule.TestScript("test_case2")]
suite3.addTests(testcases)
print(suite3)
# <unittest.suite.TestSuite tests=[<test1.TestScript testMethod=test_case1>, <test1.TestScript testMethod=test_case3>, <test.TestScript testMethod=test_case2>]>
result = unittest.TextTestResult(sys.stdout,"test_result",1)
suite3.run(result)
–test.py执行结果:
–test_all.py执行结果:
通过unittest.TestLoader类构建
2>. 通过unittest.TestLoader类的discover、loadTestsFromTestCase、loadTestsFromModule、loadTestsFromName、loadTestsFromNames这五个方法去构建
–代码结构:test.py和test_all.py位于同一目录下
–test.py代码如下:
import unittest
class TestScript(unittest.TestCase):
'''测试unittest-加入测试套件test'''
@classmethod
def setUpClass(cls) -> None:
print("TestScript测试类的前置")
def test_case1(self):
print("执行test的测试用例test_case1")
def test_case2(self):
print("执行test的测试用例test_case2")
def test_step1(self):
print("执行test的测试用例test_step1")
def test_step2(self):
print("执行test的测试用例test_step2")
class TestCaseCS(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("TestCaseCS测试类的前置")
def test_step1(self):
print("执行test的测试类TestCaseCS类的测试用例test_step1")
if __name__ == "__main__":
'''使用unittest.TestLoader类discover、loadTestsFromTestCase、loadTestsFromModule、loadTestsFromName、loadTestsFromNames'''
suite = unittest.TestLoader().discover(".","test*")
suite1 = unittest.TestLoader().discover(".","test_step*") # 未找到
suite2 = unittest.TestLoader().loadTestsFromTestCase(TestScript)
suite3 = unittest.TestLoader().loadTestsFromTestCase(TestCaseCS)
module = __import__(__name__)
# print(module) # <module '__main__' from 'D:/pyInfcAutotest/intertestframework-master-20210304/intertestframework-master/test_case/002_unittest_test/test.py'>
result = unittest.TextTestResult(sys.stdout,'test_result',1)
suite3.run(result)
–test_all.py代码如下:
import unittest
import sys
testModule = __import__("test",{},{},"test")
print(testModule)
suite = unittest.TestLoader().loadTestsFromModule(testModule)
suite1 = unittest.TestLoader().loadTestsFromNames(["TestCaseCS.test_step1","TestScript.test_case2"],testModule)
suite2 = unittest.TestLoader().loadTestsFromName("TestScript.test_step2",testModule)
result = unittest.TextTestResult(sys.stdout,"test_all_result",1)
suite2.run(result)
–test.py执行结果:(注意在执行test.py的时候还没有创建test_all.py)
–test.py执行suite结果
–test.py执行suite1结果
未找到测试用例,无结果打印
–test.py执行suite2结果
–test.py执行suite3结果
–test_all.py执行suite结果
–test_all.py执行suite1结果
–test_all.py执行suite2结果
通过unittest.makeSuite()、unittest.findTestCases()构建
3>. 通过unittest.makeSuite()、unittest.findTestCases()这两个方法去构建
–代码结构:test.py
–test.py代码如下
import unittest
class TestScript(unittest.TestCase):
'''测试unittest-加入测试套件test'''
@classmethod
def setUpClass(cls) -> None:
print("TestScript测试类的前置")
def test_case1(self):
print("执行test的测试用例test_case1")
def test_case2(self):
print("执行test的测试用例test_case2")
def test_step1(self):
print("执行test的测试用例test_step1")
def test_step2(self):
print("执行test的测试用例test_step2")
def case_test1(self):
print("执行test的测试用例case_test1")
class TestCaseCS(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("TestCaseCS测试类的前置")
def test_step1(self):
print("执行test的测试类TestCaseCS类的测试用例test_step1")
def case_test1(self):
print("执行test的测试类TestCaseCS类的测试用例case_test1")
if __name__ == "__main__":
'''通过unittest.makeSuite()、unittest.findTestCases()这两个方法去构建'''
module = __import__("test")
suite = unittest.makeSuite(TestCaseCS,prefix='test')
suite1 = unittest.makeSuite(TestCaseCS,prefix='case')
suite2 = unittest.findTestCases(module,prefix="test")
suite3 = unittest.findTestCases(module,prefix="case")
result = unittest.TextTestResult(sys.stdout,"test_result",1)
suite3.run(result)
–test.py执行suite3结果:
数据驱动/参数化
适用于测试方法不变,但是不同的参数输出不同或者相同的测试结果的场景。接口测试中可以运行的场景是参数校验的测试
ddt方式
1>ddt方式
–代码结构:test.py和test_ddt.json位于同于路径下
import unittest
import ddt
test_data = [
{"body":{
"username":"test1",
"sex":"0",
"city":"BeiJing"
},
"code":"1005",
"message":"消息格式错误"
},
{"body":{
"username":"test2",
"sex":"1",
"city":"ShangHai"
},
"code":"1005",
"message":"消息格式错误"
}
]
@ddt.ddt
class TestScript(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("测试前置")
# 测试ddt
@ddt.data(*test_data)
def test_param(self,data):
# 方式1:不通过解包方式获取请求body,响应code和响应message
print(data.get("body"))
print(data.get("code"))
print(data.get("message"))
# print(data)
@ddt.data(*test_data)
@ddt.unpack
def test_param1(self,body,code,message):
print(body)
print(code)
print(message)
@ddt.file_data("test_ddt.json")
def test_param2(self,body,code,message):
print(body)
print(code)
print(type(message))
if __name__ == "__main__":
unittest.main()
–执行test.py结果:
paramunitest方式
2>paramunitest
–目录结构:test.py和test_param.json在同一路径下
–test.py代码
import unittest
import paramunittest
import json
import requests
with open("./test_param.json","r",encoding="utf-8") as fb:
dictdata = json.load(fb)
# print(dictdata)
@paramunittest.parametrized(*dictdata)
class TestScript(unittest.TestCase):
'''测试参数化'''
@classmethod
def setUpClass(cls) -> None:
print("测试类前置")
def setParameters(self,body,code,message):
self.body = body
self.code = code
self.message = message
def test_param(self):
# 发起请求
r = requests.post("http://10.1.70.170:30894/test",json=self.body)
self.assertEqual(self.code,r.json().get("code"))
self.assertEqual(self.message,r.json().get("message"))
# print(self.body)
# print(self.code)
# print(self.message)
if __name__ == "__main__":
unittest.main(verbosity=2)
—test_param.json文件
[
{
"body": {
"tel":"151521861981",
"area_code":"028",
"expiration":"20",
},
"code": "1005",
"message": "请求消息格式错误"
},
{
"body": {
"area_code":"ss",
"expiration":"20",
"audio":"256",
},
"code": "1005",
"message": "请求消息格式错误"
}
]
–执行结果
两条测试用例跑通
生成测试结果/报告
生成测试结果/报告的两种方式:unittest自带TextTestRunner和HTMLTestRunner
TextTestRunner输出txt报告
–代码目录结构:test.py和report目录在同一层级目录
–test.py代码如下:
import unittest
import os
import time
class TestScript(unittest.TestCase):
'''测试unittest-加入测试套件test'''
@classmethod
def setUpClass(cls) -> None:
print("TestScript测试类的前置")
def test_case1(self):
print("执行test的测试用例test_case1")
def test_case2(self):
print("执行test的测试用例test_case2")
def test_step1(self):
print("执行test的测试用例test_step1")
def test_step2(self):
print("执行test的测试用例test_step2")
def case_test1(self):
print("执行test的测试用例case_test1")
class TestCaseCS(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("TestCaseCS测试类的前置")
def test_step1(self):
print("执行test的测试类TestCaseCS类的测试用例test_step1")
def case_test1(self):
print("执行test的测试类TestCaseCS类的测试用例case_test1")
if __name__ == "__main__":
module = __import__("test")
suite3 = unittest.findTestCases(module,prefix="case")
rootPath = os.path.abspath(".")
record = time.strftime("%Y-%m-%d_%H-%M-%S")
spath = os.path.join(rootPath,'report','test_'+record)
print(spath)
os.makedirs(spath)
filename = os.path.join(spath,"Test_Report.txt")
with open(filename,'w+') as f:
runner = unittest.TextTestRunner(stream=f,verbosity=2)
runner.run(suite3)
–运行test.py结果:
HTMLTestRunner输出HTML报告
–代码目录同上TextTestRunner
–使用的是HTMLTestRunner-rv 1.0.15:pip install HTMLTestRunner-rv
–test.py代码如下
import unittest
import os
import time
from HTMLTestRunner import HTMLTestRunner
class TestScript(unittest.TestCase):
'''测试unittest-加入测试套件test'''
@classmethod
def setUpClass(cls) -> None:
print("TestScript测试类的前置")
def test_case1(self):
print("执行test的测试用例test_case1")
def test_case2(self):
print("执行test的测试用例test_case2")
def test_step1(self):
print("执行test的测试用例test_step1")
def test_step2(self):
print("执行test的测试用例test_step2")
def case_test1(self):
print("执行test的测试用例case_test1")
class TestCaseCS(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("TestCaseCS测试类的前置")
def test_step1(self):
print("执行test的测试类TestCaseCS类的测试用例test_step1")
def case_test1(self):
print("执行test的测试类TestCaseCS类的测试用例case_test1")
if __name__ == "__main__":
module = __import__("test")
suite3 = unittest.findTestCases(module,prefix="case")
reportdir = os.path.join('report','test_'+time.strftime("%Y-%m-%d_%H-%M-%S"))
runner = HTMLTestRunner(log=True,verbosity=2,output=reportdir,title='Test Report',description='htmlTestReport',report_name='Test_Report.html')
runner.run(suite3)
–test.py执行结果如下:
unittest断言
异常捕捉与错误截图
待完善
unittest组成部分理解
1>整理流程理解:写好的TestCase由TestLoader加载到TestSuite,然后由TextTestRunner来运行TestSuite,运行结果保存在TextTestResult中
参考资料
1.https://blog.csdn.net/qq_41437305/article/details/96368903
2.https://blog.csdn.net/fengguangke/category_7941105.html
3.unittest官方文档:https://docs.python.org/zh-cn/3.9/library/unittest.html