Python编程 从入门到实践 第十一章 测试代码

文章介绍了Python中的单元测试和测试用例,通过unittest模块创建和运行测试。文章通过示例展示了如何编写测试函数,当测试未通过时如何修复代码,以及如何添加新测试。此外,还讲解了测试类的方法,包括各种断言方法,以及如何使用setUp()方法优化测试类的代码结构。
摘要由CSDN通过智能技术生成

1. 测试函数

  1. 编写函数或类时,还可为其编写测试

1.1 单元测试和测试用例

  1. Python标准库中的模块unittest提供了代码测试工具
  2. 单元测试:用于核实函数某个方面没有问题
  3. 测试用例:一组单元测试,核实函数在各种情形下的行为都符合要求
  4. 全覆盖式测试用例:一整套单元测试,涵盖各种可能的函数使用方式
  5. 对于大型项目,要实现全覆盖很难。通常,最初只要针对代码的重要行为编写测试即可

1.2 可通过的测试

1.演示示例如下:
name_function.py

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

test_name_function.py

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
    """测试name_function.py"""

    def test_first_last_name(self):
        """能够正确地处理像Jains Joplin这样的姓名吗?"""
        formatted_name = get_formatted_name('jains', 'joplin')
        self.assertEqual(formatted_name, 'Jains Joplin')

if __name__ == '__main__':
    unittest.main()
-------------------------------------------------------------------------------
Ran 1 tests in 0.008s

PASSED (successes=1)
  1. 上面示例中创建了一个名为NamesTestCase类,可随便给这个类命名,但最好让它看起来与测试的函数有关,并包含字样Test
  2. 上面使用了unittest类最有用的功能之一:一个断言方法

1.3 不能通过的测试

  1. 修改get_formatted_name(),使其能够处理名、中间名、姓
  2. 演示示例如下
    name_function.py
def get_formatted_name(first,middle,last):
    """生成整洁的姓名"""
    full_name = first + ' ' + middle + ' ' + last
    return full_name.title()

test_name_function.py

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
    """测试name_function.py"""

    def test_first_last_name(self):
        """能够正确地处理像Jains Joplin这样的姓名吗?"""
        formatted_name = get_formatted_name('jains', 'joplin')
        self.assertEqual(formatted_name, 'Jains Joplin')

if __name__ == '__main__':
    unittest.main()
===============================================================================
[ERROR]
Traceback (most recent call last):
Failure: builtins.tuple: (<class 'TypeError'>, TypeError("get_formatted_name() missing 1 required positional argument: 'last'"), <traceback object at 0x0000013F3E143380>)

test_name_function.NamesTestCase.test_first_last_name
-------------------------------------------------------------------------------
Ran 1 tests in 0.008s

FAILED (errors=1)
  1. 测试未通过,因为测试提供两个实参,而原函数有三个参数

1.4 测试未通过时怎么办

  1. 测试通过意味着函数的行为是对的,测试未通过意味着编写的新代码有错
  2. 测试未通过时,不要修改测试,而应该修复导致测试不能通过的代码
  3. 演示示例如下:
    name_function.py
def get_formatted_name(first,last,middle=''):
    """生成整洁的姓名"""
    if middle:
        full_name = first + ' ' + middle + ' ' + last
    else:
        full_name = first + ' ' + last
    return full_name.title()

test_name_function.py

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
    """测试name_function.py"""

    def test_first_last_name(self):
        """能够正确地处理像Jains Joplin这样的姓名吗?"""
        formatted_name = get_formatted_name('jains', 'joplin')
        self.assertEqual(formatted_name, 'Jains Joplin')

if __name__ == '__main__':
    unittest.main()
-------------------------------------------------------------------------------
Ran 1 tests in 0.007s

PASSED (successes=1)

1.5 添加新测试

1.演示示例如下:
name_function.py

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

test_name_function.py

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
    """测试name_function.py"""

    def test_first_last_name(self):
        """能够正确地处理像Jains Joplin这样的姓名吗?"""
        formatted_name = get_formatted_name('jains', 'joplin')
        self.assertEqual(formatted_name, 'Jains 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')

if __name__ == '__main__':
    unittest.main()
-------------------------------------------------------------------------------
Ran 2 tests in 0.007s

PASSED (successes=2)

Process finished with exit code 0

2. 测试类

2.1 各种断言方法

  1. 断言方法检查你认为应该满足的条件是否确实满足
  2. 如果条件确实满足,你对程序行为的假设就得到了确认,可以确信其中没有错误
  3. 如果应该满足的条件实际上并没有满足,Python将引发异常
  4. 6个常用断言方法

2.2 一个要测试的类

  1. 演示的示例如下:
    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)

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

2.3 测试Anonymous Survey 类

  1. 演示示例如下
    test_survey.py
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)

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

if __name__ == '__main__':
    unittest.main()
-------------------------------------------------------------------------------
Ran 2 tests in 0.007s

PASSED (successes=2)

2.4 方法setUp()类

1.上面例子中,在每个测试方法中都创建了一个AnonymousSurvey实例,并在每个方法中都创建了答案
2. unittest.TestCase类包含方法setUp(),我们只需创建这些对象一次,并在每个测试方法中使用它们
3. 演示示例如下:

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    """针对AnonymousSurvey类的测试"""

    def setUp(self) -> None:
        """创建一个调查对象和一组答案,供使用的测试方法使用"""
        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)

if __name__ == '__main__':
    unittest.main()
-------------------------------------------------------------------------------
Ran 2 tests in 0.007s

PASSED (successes=2)

Process finished with exit code 0
  1. 测试自己编写的类时,可在setUp()方法中创建一些列实例并设置它们的属性,再在测试方法中直接使用这些实例

3. 小结

  1. 如果你在项目中包含了初步测试,其他程序员将更敬佩你,他们将能够更得心应手地尝试使用你编写的代码,也更愿意与你合作开发项目
  2. 如果你要跟其他程序员开发的项目共享代码,就必须证明你编写的代码通过了既有测试,通常还需要为你添加的新行为编写测试
  3. 对于自己编写的函数和类,请编写针对其重要行为的测试,但在项目早期,不要试图去编写全覆盖的测试用例,除非有充分的理由这样做

4. 章节跳转

  1. Python编程 从入门到实践 第一章 起步
  2. Python编程 从入门到实践 第二章 变量和简单数据类型
  3. Python编程 从入门到实践 第三章 列表简介
  4. Python编程 从入门到实践 第四章 列表操作
  5. Python编程 从入门到实践 第五章 if语句
  6. Python编程 从入门到实践 第六章 字典
  7. Python编程 从入门到实践 第七章 用户输入和while循环
  8. Python编程 从入门到实践 第八章 函数
  9. Python编程 从入门到实践 第九章 类
  10. Python编程 从入门到实践 第十章 文件和异常
  11. Python编程 从入门到实践 第十一章 测试代码
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值