Python study day 07

7. 测试代码

7.1 测试函数

        要学习测试,就必须要有要测试的代码。

下面是一个简单的函数,他接受名和姓并返回整洁的姓名,示例:

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

为了核实函数是否能和期望那样来工作,再编写一个使用该函数的程序:

from zlx2 import get_formatted_name

print("Enter 'q' at any time quit.")
while True:
    first = input("Please 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(formatted_name)

7.1.1 单元测试和测试用例

        Python标准库中的unittest 提供了代码测试工具。单元测试用于核实该函数的某个方面没有问题。测试用例是一组单元测试,他们一道核实函数在各种情况下的行为都符合要求。良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情形的测试。全覆盖的测试用例包含一整套单元测试,覆盖率各种可能的函数的使用方式。对于大型项目,要进行全覆盖测试可能很难。通常,最初要针对代码的重要行为编写测试即可,等项目被广泛使用时再考虑全覆盖。

7.1.2 可通过的测试

        我们需要一段时间才能习惯创建测试用例的语法,但创建测试用例后,在添加针对函数的单元测试就简单了。要为函数编写测试用例,可先导入模块unittest 和要测试的函数, 再创建一个继承unittest.TestCase 的类, 并编写一系列方法对函数的行为和不同方面进行测试。

示例:

import unittest
from zlx2 import get_formatted_name

class NamesTestCase(unittest.TestCase):
    '''测试函数文件'''
    def test_first_last_name(self):
        '''能够正确地处理像zhou luxun这样的名字吗'''
        formatted_name = get_formatted_name('zhou', 'luxun')
        self.assertEquals(formatted_name, 'Zhou Luxun')
        '''
        使用了unittest 类中最有用的功能之一:断言方法,
        断言方法核实得到的结果是否也期望的结果一致。
        '''

if __name__ == '__main__':
        unittest.main()

运行程序时,得到以下输出时表明测试通过了:

        第一行的句点表明有一个测试通过了。接下来的一行指出Python运行了一个测试,消耗的时间不到0.001秒。最后的OK表明该测试用例中的所有单元测试都通过了。

7.1.3 未通过的测试

示例:

        第一行的E,指出测试用例中有一个单元测试导致了错误。后面黄方框里面的都是错误提示。

7.1.4 测试未通过时怎么办 

        测试未通过时怎么办能?如果你检查的条件没错,测试通过意味着函数的行为是对的,而测试未通过意味着编写的新代码有错。因此,测试未通过时,不要修改测试,而应修复导致测试不能通过的代码:检查刚刚对函数所做的修改,找出导致函数行为不符合预期的修改。

示例:

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()

7.1.5 添加新测试

        确定函数又能够正确处理简单的姓名后,我们再编写一个测试,用于包含中间名的姓名。为此,在类中再添加一个方法。

示例:

import unittest
from zlx2 import get_formatted_name

class NamesTestCase(unittest.TestCase):
    '''测试函数文件'''
    def test_first_last_name(self):
        '''能够正确地处理像zhou luxun这样的名字吗'''
        formatted_name = get_formatted_name('zhou', 'luxun')
        self.assertEquals(formatted_name, 'Zhou Luxun')
        '''
        使用了unittest 类中最有用的功能之一:断言方法,
        断言方法核实得到的结果是否也期望的结果一致。
        '''

    def test_first_last_modlle_name(self):
        '''能够正确地处理像Zhou LU Xun这样的姓名吗'''
        formatted_name = get_formatted_name('zhou', 'lu', 'xun')
        self.assertEqual(formatted_name, 'Zhou Lu Xun')

if __name__ == '__main__':
        unittest.main()

7.2 测试类

7.2.1 各种断言方法

unittest 模块中的断言方法:

方法用途
assertEqual(a, b)核实 a == b
assertEqual(a, b)核实 a != b
assertEqual(x)核实 x 为 Ture
assertEqual(x)核实 x 为 False
assertEqual(item, list)核实 item 在 list 中
assertEqual(item, list)核实 item 不在 list 中

7.2.2  一个要测试的类

        类的测试与函数的测试相似,我们所做的大部分工作是测试类中发方法的行为。不过还是存在一些不同之处,下面来编写一个要测试的类。

示例:

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(response)
from zlx3 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)

my_survey.show_results()

7.2.3 测试这个AnonymonusSurvey 类

        下面来编写一个测试, 对类的行为的一个方面进行验证:如果用户面对调查问题只提供一个答案,这个问题也能被妥善地存储。为此,我们将在这个答案被存储后,使用方法assertIn()来核实他确实在答案列表中。

示例:

import unittest
from zlx3 import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    def test_store_single_response(self):
            question = "What language did you first learn to speak?"
            my_surbey = AnonymousSurvey(question)
            my_surbey.store_response('English')
            self.assertIn('English', my_surbey.responses)

    def test_store_three_responses(self):
        question = "What language did you first learn to speak?"
        my_surbey = AnonymousSurvey(question)
        responses = ['English', '中文', 'Mandarin']
        for response in responses:
            my_surbey.store_response(response)

        for response in responses:
            self.assertIn(response, my_surbey.responses)

if __name__ == '__main__':
    unittest.main()


7.2.4 方法 setUp()

        在前面的例子中,我们在每个测试方法中都创建了一个AnonymousSurvey 实例, 并在每个方法中都创建了答案。unittest.TestCase 类包含的方法setUp() 让我们只需创建这些对象一次,就能在每个测试方法中使用。如果在TestCase 类中包含了方法setUp(),Python将先运行它,在运行各个以test_ 打头的方法。这样,在你编写的每个测试方法中,都可使用在方法setUp() 中创建的对象。

下面使用setUp()来创建一个调查对象和一组答案,供方法test_store_single_response()和test_store_three_responses()使用:

import unittest
from zlx3 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', '中文', '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()

注意:        运行测试用例时,每完成一个单元测试,Python都打印一个字符:测试通过时打印一个句点,测试引发错误时打印一个E,而测试导致断言失败时打印一个F。这就是我们运行测试用例时,在输出的第一行中看到的句点和字符数量各不相同的原因。如果测试用例包含很多单元测试, 需要运行很长时间,就可通过观察这些结果来获悉有多少个测试通过了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值