python-测试代码

介绍

在本章中你会学习如何使用Python模块nuittest中的工具来测试代码。你将了解测试通过和未通过的样子;同时,你将学习如何测试函数和类;和知道该为项目编写多少个测试。

测试函数

要学习测试,先得有测试的代码。下面编写了一个简单的函数,它接受名和姓并返回整洁的姓名。

name_function.py

def get_formatted_name(first,last):
    full_name=first+' '+last
    return full_name.title()

为核实get_formatted_name()像期望那样工作,我们来编写一个使用这个函数的程序。程序names.py让用户输入名和姓,并显示整洁的全名。

names.py
from name_function import get_formatted_name
print("Enter 'q' at any time to quit.")
while True:
    first = input("please input your first name:")
    if first == 'q':
        break
    last = input("please input your last name:")
    if last == 'q':
        break
    formatted_name=get_formatted_name(first,last)
    print("\tNeatly formatted name:"+formatted_name+'.')

单元测试和测试用例

python标注库中的模块nuittest提供了代码测试工具。
良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情形的测试。
单元测试用于核实函数某个方面没有问题
测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。
全覆盖测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。

可通过的测试

要为函数编写测试用例,可先导入模块unittest以及要测试的函数,再创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。
下面是一个只包含一个方法的测试用例,它检查函数get_formatted_name()在给定名和姓时是否正确地工作。

test_name_function.py

import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase):
    def test_first_last_name(self):
        formatted_name = get_formatted_name('janis', 'joplin')
        self.assertEqual(formatted_name, 'janis joplin') #3

unittest.main()

解释:类nameTestCase继承了unittest.TestCase类。
3处我们使用了unittest类最有用的功能之一:一个断言方法,断言方法用来核实得到的结果是否与预期的结果一致。比较fomatted_name的值和字符串’janis joplin’,如果他们相等,就万事大吉,如果他们不相等就跟我说一声。
结果出现句点和OK,表示测试通过了。

不能通过的测试

在name_function.py中添加一个名为new_get_formatted_name(first,middle,last)的函数,添加一个中间名。再放到test_name_function.py中测试:self.assertEqual(formatted_name, ‘janis joplin’),测试代码只改变为新的函数,其余不变。

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,他指出测试用例中有一个单元测试导致了错误。
  2. ②处表示为:NamesTestCase中的test_first_last_name()导致了错误。测试用例中包含从多单元测试时,知道那个测试未通过至关重要。
  3. 在③处,它指出函数get_formatted_name(‘janis’,‘joplin’)有问题,因为它缺少一个必不可少的位置实参。
  4. 第④处,我们可以看到运行了一个单元测试。
  5. 第⑤处,我们可以知道测试该用例一共有几个错误。

测试未通过时怎么办

如果你检查的条件没有错,测试通过了意味着函数的行为是对的,而测试没有通过意味着你编写的新代码有错。因此,测试未通过时,不要修改测试,而应修复导致测试不能通过的代码。
在这个示例中,get_formatted_name()以前只需要两个实参–姓和名,但现在它要求提供中间名。新增的中间名参数是必不可少的,这导致get_formatted_name()的行为不符合预期。故最佳选择为让中间名变为可选的。

name_function.py

def new_get_formatted_name(first, last, middle=''):
    if middle:
        full_name = first + ' ' + middle + ' ' + last
        return full_name
    else:
        full_name = first + ' ' + last
        return full_name

再进行测试,测试通过。

添加新测试

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

import unittest
from name_function import new_get_formatted_name


class NamesTestCase(unittest.TestCase):
    def test_first_last_name(self):
        formatted_name = new_get_formatted_name('janis', 'joplin')
        self.assertEqual(formatted_name, 'janis joplin')

    def test_first_last_middle_name(self):
        formatted_name = new_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时自动运行。
在TestCase类中,使用很长的方法名是可以的,这些方法名称必须是描述性的,这样才能让你明白测试未通过时的输出。
这些方法由python调用,不用你编写他们的代码。

测试类

各种断言方法

python在unittest.TestCase类中提供了很多断言的方法。前面说过,断言方法检查你认为应该满足的条件是否可以满足。如果该条件满足,你对程序的假设就得了确认,你就可以确信其中没有错误。如果你认为应该满足的条件实际并不满足,python将引发异常。
常见的断言方法有六个:

方法用途
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 中

一个要测试的类

下面来编写一个类进行测试,创建一个帮助管理匿名调查的类。

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('-'+response)

编写一个使用该类的程序

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
    else:
        my_survey.store_response(response)
print("\nThank you to everyone who participated in the survey!")
my_survey.show_results()

测试AnonymousSurvey类

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

test_survey.py

import unittest
from survey import AnonymousSurvey


class TestAnonymousSurvey(unittest.TestCase):
    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()

测试成功,但它只能收集一个答案的调查用途不大。下面来核实用户提供三个答案时,他们也将被妥善地存储。故,我们在TestAnonymousSurvey中添加一个方法。

import unittest
from survey import AnonymousSurvey


class TestAnonymousSurvey(unittest.TestCase):
    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)

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

方法setUp()

前面的测试效果很好,但有些测试有重复的地方,接下来使用unittest的另一项功能来提高它们的效率。
unittest.TestCase类包含方法setUp(),让我们只需创建这些对象一次,并在每个测试方法中使用他们。
如果你在TestCase类中包含了方法setUp(),python将先运行它,再运行各个以test_打头的方法。这样,在你编写的每个测试方法中都可使用在方法setUp()中创建对象了。

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

import unittest
from survey import AnonymousSurvey


class TestAnonymousSurvey(unittest.TestCase):
    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_response(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()

解释:方法setUp()做了两件事:创建一个调查对象;创建一个答案列表。存储这两样东西的变量名包含前缀self(即存储在属性中),因此可在这个类的任何地方使用。这让两个测试方法都更简单,因为它们都不用创建调查对象和答案。
方法setUp()让测试方法编写起来更容易:可在setUp()方法中创建一系列实例并设置它们的属性,再在测试方法中直接使用这些实例。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ee .

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值