内容简介:测试代码
测试代码
介绍如何使用 Python 模块unittest 中的 工具 来测试代码。
1. 测试函数
在Python中,测试函数是用于自动化测试,使用python模块中的unittest中的工具来进行测试。
例如,创建一个函数max_function()接受两个数字,求其最大值,再创建一个函数number_function()提示用户输入两个数
代码1:
1 def get_max_number(x,y): 2 """求两个数中的最大值""" 3 if x > y : 4 max_number = x 5 else: 6 max_number = y 7 return max_number
代码2:
1 from max_function import get_max_number 2 3 print("Enter 'q' at any time to quit.") 4 while True: 5 x = input("Please enter x:") 6 if x == 'q': 7 break 8 y = input("Please enter y:") 9 if y == 'q': 10 break 11 max_number = get_max_number(x,y) 12 print("The max is :",max_number,".")
运行结果:
1 Enter 'q' at any time to quit. 2 Please enter x:12 3 Please enter y:23 4 The max is : 23 . 5 Please enter x:66 6 Please enter y:99 7 The max is : 99 . 8 Please enter x:q
1.1 单元测试和测试用例
Python标准库中的模块 unittest 提供了代码测试工具。单元测试用于核实函数的某个方面没有问题;测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情形的测试。全覆盖式测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。对于大型项目,要实现全覆盖可能很难。通常,最初只要针对代码的重要行为编写测试即可,等项目被广泛使用时再考虑全覆盖。
1.2 可通过的测试
创建测试用例的语法需要一段时间才能习惯,但测试用例创建后,再添加针对函数的单元测试就很简单了。要为函数编写测试用例,可先导入模块 unittest 以及要测试的函数,再创建一个继承 unittest.TestCase 的类,并编写一系列方法对函数行为的不同方面进行测试。
例如,创建一个包含一个方法的测试用例,它检查函数get_max_number()在给定x、y值时,是否能正确求出最大值。
代码:
1 import unittest 2 3 from max_function import get_max_number 4 5 class TestMaxCase(unittest.TestCase): 6 """测试max_function.py""" 7 8 def test_x_y(self): 9 """能够处理x,y这样的数字""" 10 max_number = get_max_number(20,18) 11 self.assertEqual(max_number,20) 12 13 unittest.main
说明:
第1行,导入一个模块unittest。
第2行,从模块max_function中导入函数get_max_number()。
第5行,创建了一个名为TestMaxCase的类,用于包含一系列针对 get_max_number() 的单元测试。
第8行,定义一个方法test_x_y()。
第10行,调用函数get_max_number()求出最大值,并赋值给变量max_number 。
第11行,调用 unittest 的方法 assertEqual() ,并向它传递 max_number 和 20 。 代码行self.assertEqual(max_number,20)的意思是说:“将 max_number的值同字数字20进行比较,如果它们相等,就万事大吉,如果它们不相等,跟我说一声!”
第13行,调用unittest的main方法。
运行结果:
1 Ran 1 test in 0.001s 2 3 OK
从第1行可知,测试了一个函数,花费了0.001s;第3行,说明测试OK,通过测试。
1.3 不能通过的测试
例如,我们创建的一个求三个数的最大值的函数,但是只给其提供两个值。
代码1:
1 def get_max_number(x,y,z): 2 """求三个数中的最大值""" 3 if x > y and x > z: 4 max_number = x 5 elif y > z: 6 max_number = y 7 else: 8 max_number = z 9 10 return max_number
代码2:
1 import unittest 2 3 from max_function1 import get_max_number 4 5 class TestMaxCase(unittest.TestCase): 6 """测试max_function.py""" 7 8 def test_x_y(self): 9 """能够处理x,y这样的数字""" 10 max_number = get_max_number(20,18) 11 self.assertEqual(max_number,20) 12 13 unittest.main
代码2的运行结果如下:
1 Ran 1 test in 0.000s 2 3 FAILED (errors=1) 4 Launching unittests with arguments python -m unittest test_max_function1.TestMaxCase in F:\PyProject\s14\exercise\chapter_eleven 5 6 Error 7 Traceback (most recent call last): 8 File "D:\Python\Python36\lib\unittest\case.py", line 59, in testPartExecutor 9 yield 10 File "D:\Python\Python36\lib\unittest\case.py", line 601, in run 11 testMethod() 12 File "F:\PyProject\s14\exercise\chapter_eleven\test_max_function1.py", line 12, in test_x_y 13 max_number = get_max_number(20,18) 14 TypeError: get_max_number() missing 1 required positional argument: 'z'
从以上运行结果可知,由于取最大值的函数需要三个位置实参,但是只给其传了两个,少了一个,由于报错。
1.4 测试未通过时怎么办
当测试不通过时,我们不要测试区修改用于测试的函数,而应该去修改被测试的函数,使其通过测试。
例如,对1.3中测试未通过的函数进行完善。
代码1:
1 def get_max_number(x=0,y=0,z=0): 2 """求三个数中的最大值""" 3 if x > y and x > z: 4 max_number = x 5 elif y > z: 6 max_number = y 7 else: 8 max_number = z 9 10 return max_number
说明:
第1行,为了在用户不提供任何参数而调用求最大值的函数时不报错,我们给每个形参都赋予一个默认值0。
代码2:
1 import unittest 2 3 from max_function2 import get_max_number 4 5 class TestMaxCase(unittest.TestCase): 6 """测试max_function.py""" 7 8 def test_x_y(self): 9 """能够处理x,y这样的数字""" 10 max_number = get_max_number(20,18) 11 self.assertEqual(max_number,20) 12 13 unittest.main
运行结果:
1 Ran 1 test in 0.000s 2 3 OK
从以上运行结果可知,测试已通过。
1.5 添加新测试
例如,我们在以上求最大的模块中,增加一个求最小值的函数。
代码1:
1 def get_max_number(x=0,y=0,z=0): 2 """求三个数中的最大值""" 3 if x > y and x > z: 4 max_number = x 5 elif y > z: 6 max_number = y 7 else: 8 max_number = z 9 10 return max_number 11 12 def get_min_number(x=0,y=0,z=0): 13 """求三个数中的最小值""" 14 if x < y and x < z : 15 min_number = x 16 elif y < z : 17 min_number = y 18 else: 19 min_number = z 20 21 return min_number
代码2:
1 import unittest 2 3 from max_min_function import get_max_number 4 from max_min_function import get_min_number 5 6 class TestMaxCase(unittest.TestCase): 7 """测试max_function.py""" 8 9 def test_max_number(self): 10 """能够处理x,y这样的数字""" 11 max_number = get_max_number(20,18) 12 self.assertEqual(max_number,20) 13 14 def test_min_number(self): 15 """能否处理x、y、z这样的数字""" 16 min_number = get_min_number(1,13,18) 17 self.assertEqual(min_number,1) 18 19 20 unittest.main
运行结果:
1 Ran 2 test in 0.001s 2 3 OK
从以上结果可知,两测试都已通过,耗时0.001s。
2. 测试类
测试类就是针对类来编写测试,验证类是否能想我没期待的那样。如果针对类的测试通过了,我没就能确信对类所做的改进没有意外地破坏其原有的行为。
2.1 各种断言方法
Python在 unittest.TestCase 类中提供了很多断言方法。断言方法检查我没认为应该满足的条件是否确实满足。如果该条件确实满足,我们对程序行为的假设就得到了确认,我们就可以确信其中没有错误。如果我们认为应该满足的条件实际上并不满足,Python将引发异常。
常用的断言方法有:
(1)assertEqual(a, b),核实 a == b。
(2) assertNotEqual(a, b),核实 a != b。
(3)assertTrue(x),核实 x 为 True。
(4)assertFalse(x),核实 x 为 False。
(5)assertIn( item , list ),核实 item 在 list 中。
(6)assertNotIn( item , list ),核实 item 不在 list 中。
2.2 测试类
类的测试与函数的测试相似,只不过类测试所做的大部分工作都是测试类中方法的行为,但存在一些不同之处。
例如,我们创建一个管理匿名调查问卷的类AnonymousSurvey,并将其存于模块survey中,然后测试它。
(1)创建类AnonymousSurvey
代码1:
1 class AnonymousSurvey(): 2 """收集匿名调查问卷的答案""" 3 4 def __init__(self,question): 5 """存储一个问题,并为存储答案做准备""" 6 self.question = question 7 self.responses = [] 8 9 def show_question(self): 10 """显示调查问卷""" 11 print(self.question) 12 13 def store_response(self,new_response): 14 """存储单份调查问卷""" 15 self.responses.append(new_response) 16 17 def show_results(self): 18 """显示收集到的所有答案""" 19 print("Survey results:") 20 for reponse in self.responses: 21 print(('-' + reponse))
说明:
第4~7行,存储了一个指定的调查问题,并创建了一个空列表response,用于存储答案。
第9~11行,创建一个函数show_question(),用于显示调查问卷。
第13~15行,创建一个函数store_response(),用于存储单份调查问卷,即使用append()方法往答案列表中添加新答案。
第17~21行,创建一个函数show_response(),用于显示收集到的所有答案,其中使用for循环遍历答案列表response。
代码2:
1 from survey import AnonymousSurvey 2 3 # 定义一个问题,并创建一个表示调查的AnonymousSurvey对象 4 question = "What language did you first learn to speak?" 5 my_survey = AnonymousSurvey(question) 6 7 # 显示问题并存储答案 8 my_survey.show_question() 9 print("Enter 'q' at time to quit.\n") 10 while True: 11 response = input("language:") 12 if response == 'q': 13 break 14 my_survey.store_response(response) 15 16 # 显示调查结果 17 print("\nThank you to everyone who participated in the survey!") 18 my_survey.show_results()
说明:
第1行,从模块survey中导入一个类AnonymousSurvey。
第4~5行,定义一个问题,并应用该问题创建一个表示调查的AnonymousSurvey对象。
第8~14行,调用 show_question() 来显示问题,并提示用户输入答案。收到每个答案的同时将其存储起来。用户输入所有答案(输入 q 要求退出)
后。
第17~18行,显示调查结果,即调用 show_results() 来打印调查结果。
运行结果:
1 What language did you first learn to speak? 2 Enter 'q' at time to quit. 3 4 language:English 5 language:Spanish 6 language:English 7 language:Chinese 8 language:Mandarin 9 language:q 10 11 Thank you to everyone who participated in the survey! 12 Survey results: 13 -English 14 -Spanish 15 -English 16 -Chinese 17 -Mandarin
(2)测试 AnonymousSurvey 类
- 测试单个答案
代码1:
1 import unittest 2 from survey import AnonymousSurvey 3 4 class TestAnonmyousSurvey(unittest.TestCase): 5 """针对AnonymousSurvey类的测试""" 6 7 def test_store_single_response(self): 8 """测试单个答案会被妥善地存储""" 9 question = "What language did you first learn to speak?" 10 my_survey = AnonymousSurvey(question) 11 my_survey.store_response('English') 12 self.assertIn('English', my_survey.responses) 13 14 unittest.main
说明:
第1行,导入测试模块unittest。
第2行,从模块survey中导入类AnonymousSurvey。
第4行,继承模块unittest中的类TestCase创建一个测试类TestAnonmyousSurvey。
第7~12行,创建一个方法test_store_single_response来测试单个答案会被妥善地存储。
第14行,调用模块unittest中的main方法来执行测试。
运行结果:
1 Ran 1 test in 0.000s 2 3 OK
- 测试多个答案
代码2:
1 import unittest 2 from survey import AnonymousSurvey 3 4 class TestAnonmyousSurvey(unittest.TestCase): 5 """针对AnonymousSurvey类的测试""" 6 7 def test_store_single_response(self): 8 """测试单个答案会被妥善地存储""" 9 question = "What language did you first learn to speak?" 10 my_survey = AnonymousSurvey(question) 11 my_survey.store_response('English') 12 self.assertIn('English', my_survey.responses) 13 14 def test_store_three_responses(self): 15 """测试三个答案会被妥善地存储""" 16 question = "What language did you first learn to speak?" 17 my_survey = AnonymousSurvey(question) 18 responses = ['English', 'Spanish', 'Mandarin'] 19 for response in responses: 20 my_survey.store_response(response) 21 22 for response in responses: 23 self.assertIn(response, my_survey.responses) 24 25 unittest.main
运行结果:
1 Ran 2 tests in 0.000s2 3 OK
2.3 方法 setUp()的使用
在前面的test_survey.py中,我们在每个测试方法中都创建了一个 AnonymousSurvey 实例,并在每个方法中都创建了答案。 unittest.TestCase 类包含方法 setUp() ,让我们只需创建这些对象一次,并在每个测试方法中使用它们。如果你在 TestCase 类中包含了方法 setUp() ,Python将先运行它,再运行各个以test_打头的方法。这样,在我们编写的每个测试方法中都可使用在方法 setUp()中创建的对象了。
例如,使用 setUp() 来创建一个调查对象和一组答案,供方法 test_store_single_response() 和test_store_three_responses() 使用。
代码:
1 import unittest 2 from survey import AnonymousSurvey 3 4 class TestAnonymousSurvey(unittest.TestCase): 5 """针对AnonymousSurvey类的测试""" 6 def setUp(self): 7 """ 8 创建一个调查对象和一组答案,供使用的测试方法使用 9 """ 10 question = "What language did you first learn to speak?" 11 self.my_survey = AnonymousSurvey(question) 12 self.responses = ['English', 'Spanish', 'Mandarin'] 13 14 def test_store_single_response(self): 15 """测试单个答案会被妥善地存储""" 16 self.my_survey.store_response(self.responses[0]) 17 self.assertIn(self.responses[0], self.my_survey.responses) 18 19 def test_store_three_responses(self): 20 """测试三个答案会被妥善地存储""" 21 for response in self.responses: 22 self.my_survey.store_response(response) 23 for response in self.responses: 24 self.assertIn(response, self.my_survey.responses) 25 26 unittest.main
说明:
第11行,创建一个调查对象。
第12行,创建一个答案列表。
运行结果:
1 Ran 1 test in 0.000s 2 3 OK
测试自己编写的类时,方法 setUp() 让测试方法编写起来更容易:可在 setUp() 方法中创建一系列实例并设置它们的属性,再在测试方法中直接使用这些实例。相比于在每个测试方法中都创建实例并设置其属性,这要容易得多。
运行测试用例时,每完成一个单元测试,Python都打印一个字符:测试通过时打印一个句点;测试引发错误时打印一个 E ;测试导致断言失败时打印一个 F 。这就是你运行测试用例时,在输出的第一行中看到的句点和字符数量各不相同的原因。如果测试用例包含很多单元测试,需要运行很长时间,就可通过观察这些结果来获悉有多少个测试通过了。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。