目录
一、测试函数
要测试的代码
def get_formatted_name(first,last):
full_name = f"{first} {last}"
return full_name.title()
编写一个使用该函数的程序,来核实函数get_formatted_name。
from name_function import get_formatted_name
print("Enter 'q' at any time to quit.")
while True:
first = input("\nPlease give me a first name: ")
if first == 'q':
break
last = input("Please give me a last name: ")
if last == 'q':
break
formatted_name = get_formatted_name(first,last)
print(f"\tNearly formatted name: {formatted_name}.")
Please give me a first name: janis
Please give me a last name: joplin
Nearly formatted name: Janis Joplin.
Please give me a first name: q
说明合并得到的姓名无误,若还要添加中间名,可修改get_formatted_name后都进行测试:运行后一个程序names.py,但python提供了一种自动测试函数输出的高效方式。python标准库中的模块unittest提供了代码测试工具。
1.单元测试和测试用例
单元测试:用于核实函数的某个方面没有问题
测试用例:是一组单元测试,核实函数在各种情形下的行为都符合要求
全覆盖测试用例:包含一整套单元测试。通常只要对代码的重要行为编写测试即可。
2.可通过的测试
import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase):
"""测试name_function.py"""
def test_first_last_name(self):
"""能够正确的处理Janis Joplin这样的姓名吗"""
formatted_name = get_formatted_name('janis','joplin')
self.assertEqual(formatted_name,'Janis Joplin')
if __name__=='__main__':
unittest.main()
为函数编写测试用例,a先导入模块unittest和要测试的函数,
b创建一个继承unittest.TestCase类,类名随意,尽量与测试的函数相关并包含Test字样,类必须继承unittest.TestCase类,python才知道如何运行编写的测试。
c.编写一系列方法对函数行为的不同方面进行测试。def test_first_last_name(self):方法名必须以test_打头,这样它才会在运行文件时自动运行,方法名清楚的指出了测试的是哪个行为。
self.assertEqual(formatted_name,'Janis Joplin') 使用了unittest最有用的功能:断言方法,将formatted_name的值与字符串'Janis Joplin'比较
特殊变量__name__是在程序执行时设置的,如果这个文件作为主程序执行,变量__name__将设置为'__main__'
调用unittest.main( )来运行测试用例,如果文件被测试框架导入,变量__name__的值将不是__main__,就不会调用unittest.main( )
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
第一行的句点表示有一个测试通过了,最后的OK 表明测试用例中的所有单元测试都通过了。
3.未通过的测试
要测试的代码
def get_formatted_name(first,niddle,last):
full_name = f"{first} {middle} {last}"
return full_name.title()
E
======================================================================
ERROR: test_first_last_name (__main__.NamesTestCase)
能够正确的处理Janis Joplin这样的姓名吗
----------------------------------------------------------------------
Traceback (most recent call last):
File "F:\python_work\chapter_11\test_name_function.py", line 7, 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.001s
FAILED (errors=1)
E表示测试用例中有一个单元测试导致了错误;提示哪个测试未通过;Traceback指出问题
4.测试未通过时怎么办
修复导致测试不能通过的代码。上例修改:让中间名变成可选的
可在函数定义中将形参middle移到形参列表末尾,并将其默认值指定为一个空字符串;再添加一个if测试,以便根据是否提供中间名相应的创建姓名。
def get_formatted_name(first,last,middle=''):
if middle:
full_name = f"{first} {middle} {last}"
else:
full_name = f"{first} {last}"
return full_name.title()
5.添加新测试
import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase):
"""测试name_function.py"""
def test_first_last_name(self):
"""能够正确的处理Janis Joplin这样的姓名吗"""
formatted_name = get_formatted_name('janis','joplin')
self.assertEqual(formatted_name,'Janis Joplin')
def test_first_middle_last(self):
"""能够正确的处理像Wolfgang Amadeus Mozart这样的姓名吗"""
formatted_name = get_formatted_name('wolfgang','mozart','amadeus')
self.assertEqual(formatted_name,'Wolfgang Amadeus Mozart')
if __name__=='__main__':
unittest.main()
方法名必须以test_打头,这样它才会在我们运行test_name_function.py时自动运行。可以在TestCase类中使用很长的方法名,且必须是描述性的,这样你才能看懂测试未通过时的输出。这些方法由Python自动调用,你根本不用编写调用它们的代码。
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
二、测试类
1.各种断言方法
python在unittest.TestCase中提供了很多断言方法,6个常用的断言方法:
方法 | 用途 |
assertEqual(a,b) | 核实a == b |
assertNotEqual(a,b) | 核实a != b |
assertTrue(x) | 核实x为True |
assertFalse(x) | 核实x为False |
assertIn(item,list) | 核实item在list中 |
assertNotIn(item,list) | 核实item不在list中 |
2.一个要测试的类
编写一个要测试的类 (survey.py)
class AnonymousSurvey:
"""收集匿名调查问卷的答案"""
def __init__(self,question):
"""存储一个问题,并为存储答案做准备"""
self.question = question
self.responses = []
def show_question(self):
"""显示调查问卷"""
print(self.question)
def store_response(self,new_response):
"""存储单份调查问卷的答案"""
self.responses.append(new_response)
def show_results(self):
"""显示收集到的所有答案"""
print("Survey results: ")
for response in self.responses:
print(f"-{response}")
编写一个使用程序证明类能正确工作(language_survey.py)
from survey import AnonymousSurvey
#定义一个问题并创建调查
question = "What language did you first 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_response(response)
#显示调查结果
print("\nThank you to everyone who participate in the survey!")
my_survey.show_results()
What language did you first learn to speak?
Enter 'q' at any time to quit.
Language: English
Language: Spanish
Language: English
Language: Mandarin
Language: q
Thank you to everyone who participate in the survey!
Survey results:
-English
-Spanish
-English
-Mandarin
3.测试AnonymousSurvey类
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""针对AnonymousSurvey类的测试"""
def test_store_single_response(self):
"""测试单个答案会被妥善存储"""
question = "What language did you first learn to speak? "
my_survey = AnonymousSurvey(question) #创建一个实例
my_survey.store_response('English')
self.assertIn('English',my_survey.responses)
if __name__ == '__main__':
unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
这只能收集一个答案的调查,用途不大,当用户提供三个答案时,也会被妥善保存。
class AnonymousSurvey:
"""收集匿名调查问卷的答案"""
def __init__(self,question):
"""存储一个问题,并为存储答案做准备"""
self.question = question
self.responses = []
def show_question(self):
"""显示调查问卷"""
print(self.question)
def store_response(self,new_response):
"""存储单份调查问卷的答案"""
self.responses.append(new_response)
def show_results(self):
"""显示收集到的所有答案"""
print("Survey results: ")
for response in self.responses:
print(f"-{response}")
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
4.方法setUp( )
如果在TestCase类中包含了方法setUp(),Python将先运行它,再运行各个以test_打头的方法。
使用setUp()来创建一个调查对象和一组答案,供方法test_store_single_response()和test_store_three_responses()使用。
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""针对AnonymousSurvey类的测试"""
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_response(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_response(response)
for response in self.responses:
self.assertIn(response,self.my_survey.responses)
if __name__ == '__main__':
unittest.main()
方法setUp()做了两件事情:创建一个调查对象,以及创建一个答案列表,存储这两样东西的变量名包含前缀self,因此可在类的任何地方使用 。这让两个测试方法都更简单,因为它们都不用创建调查对象和答案了。
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
测试自己编写的类时,方法setUp()让测试方法编写起来更容易:可在setUp()方法中创建一系列实例并设置其属性,再在测试方法中直接使用这些实例。相比于在每个测试方法中都创建实例并设置其属性,这要容易得多。
运行测试用例时,每完成一个单元测试,Python都打印一个字符:测试通过时打印一个句点,测试引发错误时打印一个E,而测试导致断言失败时则打印一个F。