第九章 测试代码

10.1 测试函数

10.1.1 单元测试和测试用例

单元测试用于核实函数的某个方面没有问题;测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。

10.1.2 可通过的测试

要为函数编写测试用例,可先导入模块unittest以及要测试的函数,再创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。

example:

import unittest 
from name_function import get_formatted_name 

 class NamesTestCase(unittest.TestCase):        #编写类继承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')        一个断言方法
        
unittest.main()     #代码行unittest.main()让Python运行这个文件中的测试

我们运行test_name_function.py时,所有以test_打头的方法都将自动运行

代码行self.assertEqual(formatted_name, 'Janis Joplin')的意思是说:

“将formatted_name的值同字符串'Janis Joplin'进行比较,如果它们相等,就万事大吉,如果它

们不相等,跟我说一声!”

10.1.3 不能通过的测试

代码行unittest.main()让Python运行这个文件中的测试

def get_formatted_name(first, middle, last): 
    """生成整洁的姓名"""
    full_name = first + ' ' + middle + ' ' + last 
    return full_name.title()

#结果:
 E 
====================================================================== 
ERROR: test_first_last_name (__main__.NamesTestCase) 
---------------------------------------------------------------------- 
 Traceback (most recent call last): 
 File "test_name_function.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)

​ 第1行输出只有一个字母E(见Ø),它指出测试用例中有一个单元测试导致了错误。接下来,我们看到

NamesTestCase中的test_first_last_name()导致了错误(见)。测试用例包含众多单元测试时,

知道哪个测试未通过至关重要。在处,我们看到了一个标准的traceback,它指出函数调用

get_formatted_name('janis', 'joplin')有问题,因为它缺少一个必不可少的位置实参。

​ 我们还看到运行了一个单元测试(见)。最后,还看到了一条消息,它指出整个测试用例

都未通过,因为运行该测试用例时发生了一个错误(见)。这条消息位于输出末尾,让你一眼

就能看到——你可不希望为获悉有多少测试未通过而翻阅长长的输出。

10.1.4 测试未通过时怎么办

根据测试结果修改原有的函数

example:10.1.3中将中间名设置为可选的

def get_formatted_name(first, last, middle=''): 
     """生成整洁的姓名""" 
    if middle: 
        full_name = first + ' ' + middle + ' ' + last 
    else: 
        full_name = first + ' ' + last 
    return full_name.title()

#结果:
. 
---------------------------------------------------------------------- 
Ran 1 test in 0.000s 
OK

10.1.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_last_middle_name(self): 
        """能够正确地处理像Wolfgang Amadeus Mozart这样的姓名吗?""" 
        formatted_name = get_formatted_name( 
            'wolfgang', 'mozart', 'amadeus') 
         self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart')
         
unittest.main()

们将这个方法命名为test_first_last_middle_name()。方法名必须以test_打头,这样它才

会在我们运行test_name_function.py时自动运行。

10.2 测试类

10.2.1 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中

10.2.2 一个要测试的类

survey.py

class AnonymousSurvey(): 
    """收集匿名调查问卷的答案""" 
    
    def __init__(self, question): 
        """存储一个问题,并为存储答案做准备""" 
        self.question = question 
        self.responses = [] 
        
    def show_question(self): 
        """显示调查问卷""" 
        print(question) 
        
    def store_response(self, new_response): 
        """存储单份调查答卷""" 
        self.responses.append(new_response) 
        
     def show_results(self): 
        """显示收集到的所有答卷""" 
        print("Survey results:") 
        for response in responses: 
            print('- ' + response)

language_survey.py

from survey import AnonymousSurvey 

#定义一个问题,并创建一个表示调查的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 participated 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 participated in the survey! 
Survey results: 
- English 
- Spanish 
- English 
- Mandarin

10.2.3 测试AnonymousSurvery

test_survey.py

import unittest 
from survey import AnonymousSurvey 

 class TestAnonmyousSurvey(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)
        
unittest.main()

要测试类的行为,需要创建其实例

结果:

. 
---------------------------------------------------------------------- 
Ran 1 test in 0.001s 
OK

核实用户提供三个答案时,它们也将被妥善地存储。为此,我们在TestAnonymousSurvey中再添加一个方法:

import unittest 
from survey import AnonymousSurvey 

class TestAnonymousSurvey(unittest.TestCase): 
    """针对AnonymousSurvey类的测试""" 
    
    def test_store_single_response(self): 
        """测试单个答案会被妥善地存储""" 
         --snip-- 
         
    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_response(response) 
            
        for response in responses: 
            self.assertIn(response, my_survey.responses) 
            
unittest.main()

10.2.4 方法setUp()

如果你在TestCase类中包含了方法setUp(),Python将先运行 它,再运行各个以test_打头的方法。这样,在你编写的每个测试方法中都可使用在方法setUp() 中创建的对象了。

测试自己编写的类时,方法setUp()让测试方法编写起来更容易:可在setUp()方法中创建一

系列实例并设置它们的属性,再在测试方法中直接使用这些实例。相比于在每个测试方法中都创

建实例并设置其属性,这要容易得多。

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) 

unittest.main()

注意: 运行测试用例时,每完成一个单元测试,Python都打印一个字符:测试通过时打印一个

句点;测试引发错误时打印一个E;测试导致断言失败时打印一个F。

你可能感兴趣的:(第九章 测试代码)