测试代码

栏目: Python · 发布时间: 7年前

内容简介:测试代码

测试代码

介绍如何使用 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 。这就是你运行测试用例时,在输出的第一行中看到的句点和字符数量各不相同的原因。如果测试用例包含很多单元测试,需要运行很长时间,就可通过观察这些结果来获悉有多少个测试通过了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ordering Disorder

Ordering Disorder

Khoi Vinh / New Riders Press / 2010-12-03 / USD 29.99

The grid has long been an invaluable tool for creating order out of chaos for designers of all kinds—from city planners to architects to typesetters and graphic artists. In recent years, web designers......一起来看看 《Ordering Disorder》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器