1、函数测试
Python标准库中的模块unittest提供了代码测试工具。
单元测试用于核实函数的某个方面没有问题;
测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。
为函数编写测试用例,可先导入模块unittest以及要测试的函数,再创建一个继承unittest的类,并编写一些列方法对函数行为的不同方法进行测试。
下面是只包含一个方法的测试用例,它检查函数get_formatted_name()在给定名和姓时,能否正确的工作:
name_function.py模块:
def get_formatted_name(first, last):
"""Generate a neatly formatted full name ."""
full_name = first + " " + last
return full_name.title()
test_name_function.py 测试函数:
import unittest
from name_functon import get_formatted_name
class NamesTestCase(unittest.TestCase): #继承类unittest.TestCase
"""测试name_function.py"""
def test_first_last_name(self):
"""能够正确处理像JanisJoplin这样的名字吗"""
formatted_name = get_formatted_name('Janis', 'joplin')
self.assertEqual(formatted_name, 'Janis Joplin') #断言方法:用来核实得到的结果是否与期望一致
unittest.main()
>>>
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
不能通过的测试:
修改get_formatted_name 函数,增加一个中间名,代码如下:
def get_formatted_name(first, middle, last):
"""Generate a neatly formatted full name ."""
full_name = first + " " + middle + ' ' + last
return full_name.title()
测试函数不做修改,必然会发生错误,看看测试未通过时,会是什么样子:
E
======================================================================
ERROR: test_first_last_name (__main__.NamesTestCase)
能够正确处理像JanisJoplin这样的名字吗
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:/Users/18969/Desktop/hahh/xx.py", line 8, in test_first_last_name
formatted_name = get_formatted_name('Janis', 'joplin')
TypeError: get_formatted_name() missing 1 required positional argument: 'last'
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
测试未通过时怎么办?
如果你检查的条件没错,测试通过了意味着函数的行为是对的,而测试未通过意味着你编写的新代码有错。因此,测试未通过时,不要修改测试,而应修复导致测试不能通过的代码:检查刚对函数所作的修改,找出导致函数行为不符合预期的修改。
修改后的name_function.py 的代码如下:
def get_formatted_name(first, last, middle=""):
"""Generate a neatly formatted full name ."""
if middle:
full_name = first + " " + middle + ' ' + last
else:
full_name = first + " " + last
return full_name.title()
测试代码不变,测试结果如下:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
添加新测试:
import unittest
from name_functon import get_formatted_name
class NamesTestCase(unittest.TestCase):
"""测试name_function.py"""
def test_first_last_name(self):
"""能够正确处理像JanisJoplin这样的名字吗"""
formatted_name = get_formatted_name('Janis', 'joplin')
self.assertEqual(formatted_name, 'Janis Joplin')
def test_first_last_middle_name(self):
"""能够正确处理像Wolfgang Amadeus Mozart这样的名字吗?"""
formatted_name = get_formatted_name('wolfgang', 'mozart', 'amadeus')
self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart')
unittest.main()
>>>
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
2、测试类
断言方法:
Python在unittest.TestCase类中提供了很多断言方法。断言方法检查你认为该满足的条件是否确实满足。如果该条件满足,你对程序的的假设就得到了确认,你就可以确信其中没有错误。如果你认为的应该满足的条件并不满足,Python将引发异常。
unittest Module 中的常用断言方法:
assertEqual(a, b) 核实 a == b
assertNotEqual(a, b) 核实a != b
assertTure(x) 核实x为True
assertFalse(x) 核实x为False
assertIn(item, list) 核实item在list中
这些断言方法只能在继承unittest.TestCase的类中使用这些方法。
一个要测试的类:
class AnonymousSurvey():
"""收集匿名调查问卷的答案"""
def __init__(self, question):
"""存储一个问题,并为存储的答案做准备"""
self.question = question
self.responses = []
def show_question(self):
"""显示调查问卷"""
print(self.question)
def store_responses(self, new_response):
"""存储调查问卷"""
self.responses.append(new_response)
def show_results(self):
"""显示收集到的所有答案"""
print("Survey results: ")
for response in self.responses:
print('- ' + response)
此类的使用程序:
from survey import AnonymousSurvey
#定义一个问题, 并创建一个表示调查的Anonymous对象
question = "What language did you learn to speak?"
my_survey = AnonymousSurvey(question)
#显示问题并存储答案
my_survey.show_question()
print("Enter 'q' at any time to quit.\n")
while True:
response = input("Language: ")
if response == "q":
break
my_survey.store_responses(response)
#显示调查结果
print("\nThank you to everyone who participated in the survey!")
my_survey.show_results()
>>>
What language did you learn to speak?
Enter 'q' at any time to quit.
Language: English
Language: French
Language: Spanish
Language: Chinses
Language: q
Thank you to everyone who participated in the survey!
Survey results:
- English
- French
- Spanish
- Chinses
测试AnonymousSurvey类:
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""针对Anonymous类的测试"""
def test_store_single_response(self):
"""测试单个答案会被妥善保存"""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
my_survey.store_responses('English')
self.assertIn('English', my_survey.responses)
def test_store_three_responses(self):
"""测试三个答案会被妥善地存储"""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
responses = ['English', 'Spanish', 'Mandarin']
for response in responses:
my_survey.store_responses(response)
for response in responses:
self.assertIn(response, my_survey.responses)
unittest.main()
>>>
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
前述做法很好,但这些测试有些重复的地方,下面使用unittest的另一项功能来提高他们的效率。
方法setUp():
前面的测试中,每个测试方法都创建了一个AnonymousSurvey实例,并在每个方法中都创建了答案。unittest.TestCase类包含方法setup(),让我们只需创建这些对象一次,并在每个测试方法中使用他们。
如果你在TestCase类中包含了方法setUp(), Python将先运行它,再运行各个以test_打头的方法。
下面使用setUp()来创建一个调查对象和一组答案,供方法test_store_single_response()和test_store_three_responses()使用:
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""针对Anonymous类的测试"""
def setUp(self):
"""
创建一个调查对象和一组答案,供使用的测试方法使用
"""
question = "What language did you first learn to speak?"
self.my_survey = AnonymousSurvey(question)
self.responses = ['English', 'Spanish', 'Mandarin']
def test_store_single_response(self):
"""测试单个答案会被妥善保存"""
self.my_survey.store_responses(self.responses[0])
self.assertIn(self.responses[0], self.my_survey.responses)
def test_store_three_responses(self):
"""测试三个答案会被妥善地存储"""
for response in self.responses:
self.my_survey.store_responses(response)
for response in self.responses:
self.assertIn(response, self.my_survey.responses)
unittest.main()
>>>
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
方法setUp()做了两件事情:创建一个调查对象;创建一个答案列表。存储这两样对象的变量名都包含前缀self(即存储在属性中),因此可在这个类的任何地方使用。
测试自己编写的类时,方法setUp()让测试方法编写起来更容易:可在setUp方法中创建一系列实例并设置他们的属性,再在测试方法中直接使用这些实例。
##运行测试用例时,每完成一个单元测试,Python都打印一个字符:测试勇敢时打印一个句点;测试引发错误时打印一个E;测试导致断言失败时打印一个F。这就是你运行测试用例时,在输出的第一行看到的句点和字符数量各不相同的原因。如果测试用例包含很多单元测试,需要运行很长时间,就可通过观察这些结果来获悉有多少个测试通过了。