unittest单元测试框架简介
1、TestFixture:测试固件。用于处理初始化的操作。
2、TestCase:测试用例。在unittest中,一个测试用例是一个独立的测试单元。它检查输入特定的数据时的响应。我们可以使用unittest提供的一个基类TestCase来创建测试用例。
3、TestSuite:测试套件。可以根据所测试的特性,将测试用例组合在一起执行。
4、TestRunner:测试执行。用于执行和输出测试结果。
从上图中,我们大概可以了解unittest测试框架在自动化测试中的整个流程。首先,我们需要设计出优秀的测试用例。同时,我们可以使用TestFixture(测试固件)来在其中做一些初始化的工作,如打开浏览器,关闭浏览器。然后我们需要将需要执行的测试用例加入到一个TestSuite(测试套件)中,将这些用例组合在一起使用TestRunner进行执行。在输出结果后,我们还需要输出TestReport(测试报告)
1 import unittest 2 3 class BaiduTest(unittest.TestCase): 4 def setUp(self): 5 print('setUp') 6 7 def tearDown(self): 8 print('tearDown') 9 10 def test_001(self): 11 print('这是第一条测试用例') 12 13 def test_002(self): 14 print('这是第二条测试用例') 15 16 if __name__ == '__main__': 17 unittest.main(verbosity=2)
运行后得到结果:
setUp 这是第一条测试用例 tearDown setUp 这是第二条测试用例 tearDown test_001 (__main__.BaiduTest) ... ok test_002 (__main__.BaiduTest) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
从输出结果中,我们可以看出先执行了setUp()然后执行测试用例,再执行tearDown()。不过,每次执行完一条测试用例后,测试固件都会重复的执行一次。如果我们有N个测试用例,那么也就意味着会重复执行N次测试固件的内容。比如需要测试一个网页,如果我们一只重复的打开关闭网页,都会占用一定的资源和时间,所以这并不是一个理想的选择
1 import unittest 2 3 class BaiduTest(unittest.TestCase): 4 @classmethod 5 def setUpClass(cls): 6 print('setUpClass') 7 8 @classmethod 9 def tearDownClass(cls): 10 print('tearDownClass') 11 12 def test_001(self): 13 print('这是第一条测试用例') 14 15 def test_002(self): 16 print('这是第二条测试用例') 17 18 if __name__ == '__main__': 19 unittest.main(verbosity=2)
运行后得到结果:
setUpClass 这是第一条测试用例 这是第二条测试用例 tearDownClass test_001 (__main__.BaiduTest) ... ok test_002 (__main__.BaiduTest) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
我们可以在结果中看到,先执行了setUpClass(),然后在执行完所有测试用例之后,执行tearDownClass()
1 import unittest 2 3 class BaiduTest(unittest.TestCase): 4 @classmethod 5 def setUpClass(cls): 6 print('setUpClass') 7 8 @classmethod 9 def tearDownClass(cls): 10 print('tearDownClass') 11 12 def test_001(self): 13 print('这是第一条测试用例') 14 15 def test_002(self): 16 print('这是第二条测试用例') 17 18 if __name__ == '__main__': 19 '''实例化TestSuite类''' 20 suite = unittest.TestSuite() 21 '''调用addTest方法,分别加入测试用例''' 22 suite.addTest(BaiduTest('test_002')) 23 suite.addTest(BaiduTest('test_001')) 24 unittest.TextTestRunner(verbosity=2).run(suite)
运行后得到结果:
setUpClass 这是第二条测试用例 这是第一条测试用例 tearDownClass test_002 (__main__.BaiduTest) ... ok test_001 (__main__.BaiduTest) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
从上面的输出结果中,我们看到测试用例不再像之前一样先执行test_001,再执行test_002,而是按照我们加入到测试套件中的顺序进行执行。但是在实际的自动化测试实行中,测试用例往往多达上千个,所以这样一个一个地加入到测试套件中手动执行的方式显然是不合理的
import unittest class BaiduTest(unittest.TestCase): @classmethod def setUpClass(cls): print('setUpClass') @classmethod def tearDownClass(cls): print('tearDownClass') def test_001(self): print('这是第一条测试用例') def test_002(self): print('这是第二条测试用例') if __name__ == '__main__': '''实例化TestSuite类,并在类中调用makeSuite方法''' suite = unittest.TestSuite(unittest.makeSuite(BaiduTest)) unittest.TextTestRunner(verbosity=2).run(suite)
运行后得到结果:
setUpClass 这是第一条测试用例 这是第二条测试用例 tearDownClass test_001 (__main__.BaiduTest) ... ok test_002 (__main__.BaiduTest) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
1 import unittest 2 3 class BaiduTest(unittest.TestCase): 4 @classmethod 5 def setUpClass(cls): 6 print('setUpClass') 7 8 @classmethod 9 def tearDownClass(cls): 10 print('tearDownClass') 11 12 def test_001(self): 13 print('这是第一条测试用例') 14 15 def test_002(self): 16 print('这是第二条测试用例') 17 18 if __name__ == '__main__': 19 suite = unittest.TestLoader().loadTestsFromTestCase(BaiduTest) 20 unittest.TextTestRunner(verbosity=2).run(suite)
运行结果如下:
setUpClass 这是第一条测试用例 这是第二条测试用例 tearDownClass test_001 (__main__.BaiduTest) ... ok test_002 (__main__.BaiduTest) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
可以看出输出结果仍然与之前是一模一样的,不过在上面的代码中,我们是直接使用了TestLoader类,TestLoader类加载测试类,并将它们返回到TestSuite中。
1 import unittest 2 3 class BaiduTest(unittest.TestCase): 4 @classmethod 5 def setUpClass(cls): 6 print('setUpClass百度') 7 8 @classmethod 9 def tearDownClass(cls): 10 print('tearDownClass百度') 11 12 def test_001(self): 13 print('这是第一条测试百度的用例') 14 15 def test_002(self): 16 print('这是第二条测试百度的用例')
test_google.py文件代码如下:
1 import unittest 2 3 class GoogleTest(unittest.TestCase): 4 @classmethod 5 def setUpClass(cls): 6 print('setUpClass谷歌') 7 8 @classmethod 9 def tearDownClass(cls): 10 print('tearDownClass谷歌') 11 12 def test_001(self): 13 print('这是第一条测试谷歌的用例') 14 15 def test_002(self): 16 print('这是第二条测试谷歌的用例')
run_all.py文件的代码如下:
1 import unittest 2 import os 3 4 5 def allCase(): 6 '''将测试用例文件加载到测试套件中''' 7 suite = unittest.TestLoader().discover( 8 start_dir=os.path.dirname(__file__), 9 pattern='test_*.py', 10 top_level_dir=None 11 ) 12 return suite 13 14 if __name__ == '__main__': 15 unittest.TextTestRunner(verbosity=2).run(allCase())
在run_all.py文件中,我们使用TestLoader这个类中的discover()方法来批量获取测试模块。在discover方法中有三个参数,分别是用来表示测试模块路径的start_dir,pattern用来获取testCase包中所有以test开头的测试用例文件,top_level_dir表示测试模块的顶层目录,如果没有顶层目录,默认为None。

上面的表格是一些我们常用的断言方法,那么来简单地看个例子:
1 import unittest 2 3 4 class lagouTest(unittest.TestCase): 5 def test_lagou(self): 6 r = {'id': '1', 'name': '张三'} 7 self.assertEqual('张三', r['name']) 8 9 10 if __name__ == '__main__': 11 unittest.main(verbosity=2)
运行后得到结果:
1 Ran 1 test in 0.001s 2 3 OK
assertEqual()是用来验证两个值是否相等的。在这个r的字典中,name的值是张三,然后我们使用assertEqual()方法来验证我们期望的结果是否与实际的结果相等,运行后我们可以在返回结果看到这次的验证是通过的。
那假设不通过呢?我们看下下面的例子:
1 import unittest 2 3 4 class lagouTest(unittest.TestCase): 5 def test_lagou(self): 6 r = {'id': '1', 'name': '李四'} 7 self.assertEqual('张三', r['name']) 8 9 10 11 if __name__ == '__main__': 12 unittest.main(verbosity=2)
运行后得到结果:
Ran 1 test in 0.004s FAILED (failures=1) 李四 != 张三 Expected :张三 Actual :李四 <Click to see difference> Traceback (most recent call last): File "/Applications/PyCharm.app/Contents/helpers/pycharm/teamcity/diff_tools.py", line 32, in _patched_equals old(self, first, second, msg) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 839, in assertEqual assertion_func(first, second, msg=msg) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 1220, in assertMultiLineEqual self.fail(self._formatMessage(msg, standardMsg)) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 680, in fail raise self.failureException(msg) AssertionError: '张三' != '李四' - 张三 + 李四
我们将字典r中的张三改成了李四,但是在断言中,我们预期结果仍然是张三,这个时候显然是与实际的结果是不符合的。我们运行后程序之后,python也很清晰的告诉我们最终的结果是失败的,同时也告诉了我们李四 != 张三。
自动化测试最终的目的我们需要得到一个结果,所以我们需要用一个测试报告来清晰的体现出我们最终的测试结果。这里我们需要借助第三方库HTMLTestRunner.py,需要的可以通过下面的地址下载。
https://pan.baidu.com/s/1oD5E1o7LhkASx5U_vqZUQQ
下载文件后,把该文件放到python安装路径的Lib子文件夹中就可以在程序中使用它来。
我们创建一个report的文件夹来存放测试报告,接下来完善我们上面的run_all.py文件。代码如下:
1 import unittest 2 import os 3 import HTMLTestRunner 4 import time 5 6 '''创建测试套件''' 7 def allCase(): 8 suite = unittest.TestLoader().discover( 9 start_dir=os.path.dirname(__file__), 10 pattern='test_*.py', 11 top_level_dir=None 12 ) 13 return suite 14 15 '''获取当前时间''' 16 def now_time(): 17 nowtime = time.strftime('%Y-%m-%d %H-%M-%S', time.localtime()) 18 return nowtime 19 20 def run(): 21 '''获取当前文件的上一个目录,作为文件''' 22 report_name = os.path.join(os.path.dirname(__file__), 'report', 'report'+now_time()+'.html') 23 print('请稍后,测试报告生成中。。。') 24 fp = open(report_name, 'wb') 25 runner = HTMLTestRunner.HTMLTestRunner( 26 stream=fp, 27 title='自动化测试报告', 28 description='这是一个测试报告的例子' 29 ) 30 runner.run(allCase()) 31 fp.close() 32 33 if __name__ == '__main__': 34 run()
运行后得到结果:
请稍后,测试报告生成中。。。 setUpClass百度 tearDownClass百度 setUpClass谷歌 tearDownClass谷歌 .... Time Elapsed: 0:00:00.079354 Process finished with exit code 0
同时在report文件夹下生成了一个html测试报告,如图:



浙公网安备 33010602011771号