单元测试的本质
测试函数,从代码级别上进行测试
单元测试的框架
python+unittest
unittest模块
python自带不需安装
-
结构
- 写用例 Testcase --- 方法
- 执行用例
- 1.TestSuite 存储用例 --- 类
- 2.TestLoader 找用例,加载用例,存到1中的TestSuite --- 类
- 对比实际结果 断言 Assert
- 出具测试报告 TextTestRunner
-
测试自己的类
python基础之初识类与对象 超继承示例中的MathMethod类
#测试MathMethod类 from py_0909.super import MathMethod #导入待测的类 import unittest class TestMathMethod(unittest.TestCase):#继承与unittest的Testcase方法 #编写测试用例 def test_add_two_positive(self): #测试两个正数相加 res=MathMethod(1,1).add() #实例化调用待测的方法 print("1+1的结果是:",res) def test_add_two_zero(self): #测试两个0相加 res = MathMethod(0, 0).add() print("0+0的结果是:", res) def test_add_two_negative(self): #测试两个负数相加 res = MathMethod(-1, -1).add() print("-1+(-1)的结果是:", res) if __name__ == '__main__': unittest.main() #unitest.main()函数用来测试类中以test开头的测试用例
运行测试用例的结果:
-
说明
一个用例是一个函数,不能传参,只有self关键字
所有的用例都是test开头,test_,例如test_add_two_positive
Testcase默认的执行顺序是按函数名的ASCII码
配置测试用例的执行顺序
如果想测试用例不按默认的执行顺序,可以通过TestSuite类、TestLoader类搭配使用配置测试用例执行顺序
-
TestSuite类说明(command键+鼠标左键进入)
TestSuite测试套件是由测试用例组成的复合测试。
要使用,请创建testsuite的实例,然后添加测试用例实例。使用suite.add('方法名')添加测试用例
添加完测试用例后,将TestSuite传递给TextTestRunner
它将按添加顺序运行各个测试用例
-
执行少量用例时,配置测试用例顺序
#新建一个py文件 import unittest from py_inter0913.unittest_01 import TestMathMethod #导入写了测试用例的类 #创建TestSuite实例,存储测试用例 suite=unittest.TestSuite() #构造测试套件 suite.addTest(TestMathMethod('test_add_two_positive')) #添加测试用例,必须传入参数--函数名 suite.addTest(TestMathMethod('test_add_two_zero')) suite.addTest(TestMathMethod('test_add_two_negative')) #执行用例 runner=unittest.TextTestRunner() runner.run(suite)
运行结果: 按我们添加测试用例的顺序执行
-
执行大量用例的方法,使用TestLoader
import unittest from py_inter0913.unittest_01 import TestMathMethod #导入写了测试用例的类 #创建一个加载器 suite=unittest.TestSuite() #创建TestSuite实例,存储测试用例 loader=unittest.TestLoader() suite.addTest(loader.loadTestsFromTestCase(TestMathMethod)) # 从测试类里去找 import也需要导到模块处 搭配loadTestsFromModule使用 # from py_inter0913 import unittest_01 # suite.addTest(loader.loadTestsFromModule(unittest_01)) #执行用例 runner=unittest.TextTestRunner() runner.run(suite)
-
setUp()和tearDown()
在测试类中添加setUp()、tearDown()方法,会在单条测试用执行开始前和结束后执行。
- setUp()、tearDown()是TestCase里的方法,写在测试类中,就是方法的重写。
class TestMathMethod(unittest.TestCase): def setUp(self): print("准备开始执行测试用例了") def tearDown(self): print("结束了") #编写测试用例 def test_add_two_positive(self): #测试两个正数相加 res=MathMethod(1,1).add() print("1+1的结果是:",res)
- 执行顺序是:
setUp->testA->tearDown->setUp->testB>tearDown
断言
断言就是预期结果。如果不加断言,没有结果对比,需要手动去检查运行的结果是否符合预期。
-
assertEqual()
def assertEqual(self, first, second, msg=None): """Fail if the two objects are unequal as determined by the '==' operator. """
- first 预期结果
- second 实际结果
- msg 出错时,输出的错误信息,可不填
-
断言使用
在测试用例中写入断言
from py_0909.super import MathMethod #导入待测的类 import unittest #测试MathMethod类 class TestMathMethod(unittest.TestCase): #编写测试用例 def test_add_two_positive(self): #测试两个正数相加 res=MathMethod(1,1).add() print("1+1的结果是:",res) self.assertEqual(2,res,"两个正数相加出错了") #断言 def test_add_two_zero(self): #测试两个0相加 res = MathMethod(0, 0).add() print("0+0的结果是:", res) self.assertEqual(0, res, "两个0相加出错了") #断言 def test_add_two_negative(self): #测试两个负数相加 res = MathMethod(-1, -1).add() print("-1+(-1)的结果是:", res) self.assertEqual(-2, res, "两个负数相加出错了") #断言 if __name__ == '__main__': unittest.main()
-
其他断言语法
-
断言的异常处理
from py_0909.super import MathMethod #导入待测的类 import unittest #测试MathMethod类 class TestMathMethod(unittest.TestCase): #编写测试用例 def test_add_two_positive(self): #测试两个正数相加 res=MathMethod(1,1).add() print("1+1的结果是:",res) try: #断言的异常处理 self.assertEqual(0,res,"两个正数相加出错了") #断言 except AssertionError as e: print("断言出错,错误是:{0}".format(e)) #对异常的处理:输出错误语句、存储到excel等 raise e #异常处理完要抛出去,否则输出结果中不会报错 def test_add_two_zero(self): #测试两个0相加 res = MathMethod(0, 0).add() print("0+0的结果是:", res) try: self.assertEqual(1, res, "两个0相加出错了") #断言 except AssertionError as e: print("断言出错,错误是:{0}".format(e)) raise e def test_add_two_negative(self): #测试两个负数相加 res = MathMethod(-1, -1).add() print("-1+(-1)的结果是:", res) try: self.assertEqual(-1, res, "两个负数相加出错了") #断言 except AssertionError as e: print("断言出错,错误是:{0}".format(e)) raise e if __name__ == '__main__': unittest.main()
- 从错误的输出可以知道,异常的基类是AssertionError。
- 捕获异常,要对异常进行处理,处理之后,要将异常抛出,否则在结果报告中,不会报错。
raise e
测试报告
-
TextTestRunner
TextTestRunner是一个以文本形式展示测试结果的测试运行程序类
- stream 输出报告的路径,默认输出控制台
- verbosity 控制输出报告的详细程度,从0-2,越来越详细
import unittest from py_inter0913.unittest_01 import TestMathMethod #导入写了测试用例的类 suite=unittest.TestSuite() #创建TestSuite实例,存储测试用例 loader=unittest.TestLoader() suite.addTest(loader.loadTestsFromTestCase(TestMathMethod)) #执行用例 with open("result.txt","w+") as file: runner=unittest.TextTestRunner(stream=file,verbosity=2) runner.run(suite)
-
HTMLTestRunner
使用unittest自带的报告不够美观。HTMLTestRunner是Python标准库的unittest模块的扩展。它生成易于使用的HTML测试报告。
- HTMLTestRunner是Python标准库的unittest模块的扩展,无法通过pip安装
- 下载HTMLTestRunner.py放在lib目录下,下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html
- 导入模块
import HTMLTestRunner
import HTMLTestRunner import unittest from py_inter0913.unittest_01 import TestMathMethod #导入写了测试用例的类 suite=unittest.TestSuite() #创建TestSuite实例,存储测试用例 loader=unittest.TestLoader() suite.addTest(loader.loadTestsFromTestCase(TestMathMethod)) with open("result.html","wb") as file: runner=HTMLTestRunner.HTMLTestRunner(stream=file, title="MathMethod测试", verbosity=2,) runner.run(suite)
运行之后会发现报错:No module named 'StringIO',原因是自己使用python3,而下载的HTMLTestRunner适用于Python2.X,需要自己对模块进行修改。
参考文档:https://www.bbsmax.com/A/QW5YL9jeJm/
- html格式的报告:
实战中问题
-
SSLError:http://2.python-requests.org/zh_CN/latest/user/advanced.html#ssl
方法1.在参数中加入
verify=False
可以不进行安全验证,requests.get('https://github.com', verify=True)
方法2.为 verify 传入 CA_BUNDLE 文件的路径,或者包含可信任 CA 证书文件的文件夹路径。
requests.get('https://github.com', verify='/path/to/certfile')
- 作业思路
- 测试用例.py
- 实例化调用被测类的方法进行测试用例编写
- 断言
- 异常处理
- 测试套件.py
- 存储要运行的测试用例
- 执行测试套件中的测试用例
- 根据测试套件.py中生成测试报告
- cookie处理
- 相当于面试题:后面的用例需要用到前面返回的某些值如何解决?
- 方法1:setUp()方法
大部分请求都要带着cookie,可以写一个登录的用例到`def setUp()`中,每一条用例都采用登录的cookie
```
def setUp():
response=request.get(url,data)
cookie=response.request.cookie
def add(self,url,data,cookie):
...
```
- 方法2:全局变量
```
COOKIE=None #全局变量
class TestHttp(unittest.Testcase):
def add(self):
golbal COOKIE #声明全局变量
response=request.get(url,data)
if response.cookies: #如果response.cookies有值
COOKIE=response.cookies #给全局变量赋新值
```
缺点是关联性太强,比如登录失败,其他都跪了
- 方法3:反射机制 setattr\hasattr\getattr\delattr
`setattr(类名,"属性名","属性值")`
```
# 示例
class GetData:
Cookie="小郭"
if __name__ == '__main__':
print(GetData.Cookie)
setattr(GetData,"Cookie","小黄")#set类中的属性值进行修改,attr--attribute
print(GetData.Cookie)
print(hasattr(GetData,"Cookie"))#判断是否有这个属性
print(getattr(GetData,"Cookie"))#获取这个属性的值
delattr(GetData,"Cookie") #删除这个属性
print(hasattr(GetData,"Cookie"))
```
实战应用:
```
#get_data.py文件
class GetData:
Cookie=None
```
```
#测试用例.py
from xxx import get_data
class TestHttp(unittest.Testcase):
def add(self):
golbal COOKIE #声明全局变量
response=request.get(url,data,getattr(GetData,"Cookie"))
```
unittest总结
-
unittest原理
-
unittest核心步骤
- testcase 一个testcase的示例就是一条测试用例
- testsuite 多个测试用例组成一个suite
- 方法一:suite.addTest("测试用例的函数名")
- 方法二:TestLoader
```
loader=unittest.TestLoader()
#该测试类中以test_开头的用例都加载进来
suite.addTest(loader.loadTestFromTestCase(测试用例的类名))
#该模块中以test_开头的用例都加载进来,一个模块可能有多个测试类,需要import该模块
suite.addTest(loader.loadTestFromModule(模块名))
```
- testrunner 执行用例
```
runner=unittest.TextTestRunner()
runner.run(suite)
```
- test fixture