python学习笔记(十八)测试函数

本文介绍Python中的单元测试概念,包括单元测试、测试用例和全覆盖式测试,通过实例演示如何使用unittest模块进行测试,以及如何处理测试通过和未通过的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python学习笔记(十八)测试函数

1、测试初见
每一个具有实用意义的程序都需要编写函数或者类,但是从主观角度,我们往往难以判断这些函数或者类面对不同的输入,是否都能够按要求进行操作。
幸运的是,python中的模块unittest为我们提供了相应的测试工具,我们可以根据这些工具编写测试代码,在用户发现问题之前就把它们测试出来。这里我们简单了解一下有关测试的几个概念:
a. 单元测试:用于核实函数的某个方面没有问题,注意这里的“某个方面”,编写单元测试最主要的地方就在于“不要贪”。
b. 测试用例:一组单元测试。测试用例的编写原则应当是综合考虑函数可能收到的各种输入。
c. 全覆盖式测试:包含一整套单元测试。全覆盖式测试的编写原则应当是包含各种可能的函数使用方式。

2、测试通过
下面我们根据例子来简单的学习一下测试代码的编写,首先,创建一个空文件夹day_18。
在这里插入图片描述

下面在文件 name_function.py 中编写待测试函数get_formatted_name(),并将其存放在文件夹day_18中。

def get_formatted_name(first, last):
	"""将传入的名和姓合并成完整姓名,并在名和姓之间加上一个空格,将其首字母大写,最终将结果返回"""
	full_name = first + ' ' + last
	return full_name.title()

接下来我们重新创建一个 test_name_function.py 文件(存放在day_18中),编写测试代码。

import unittest
from name_function import get_formatted_name

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

	def test_first_last_name(self):
		"""测试能否正确处理样例姓名"""
		formatted_name = get_formatted_name('python', 'java')
		self.assertEqual(formatted_name, 'Python Java')

unittest.main()

我们先来看代码,编写测试代码文件一般有一些固定的小套路:
a. 导入模块unittest,因为我们在编写测试类的时候需要继承模块unittest中的类(主要是TestCase,为了告诉python如何运行我们编写的测试)。
b. 导入待测试函数。
c. 创建测试类(这个类的命名是随意的,但是最好联系待测试函数,并包含"Test"),继承unittest.TestCase,编写一系列方法测试函数行为的不同方面(注意不重不漏)。这里注意,当我们运行测试代码文件时,所有以 test_ 开头的方法都将自动运行。
d. 测试文件的最后一般都是 unittest.main() ,它让python运行这个文件中的测试。

上述代码只自定义了一个方法,因此它只用于测试待测试函数的一个方面(只有名和姓的输入能否被正确地操作)。这个方法中最重要的就是我们使用了unittest类最有用的功能之一——断言方法(用来核实得到的结果是否与期望值一致)。接下来我们来看运行结果:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

第一行的句点表示有一个测试通过了,然后指出python运行一个测试所消耗的时间,最后的OK表明该测试用例中的所有单元测试都通过了。

3、测试未通过
下面我们改写 name_function.py 文件中的 get_formatted_name() 函数,看一下测试未通过的运行结果。

def get_formatted_name(first, middle, last):
	"""生成完整的名字"""
	# 这里函数用来处理有中间的姓名
	full_name = first + ' ' + middle + ' ' + last
	return full_name.title()

重新运行 test_name_function.py 文件,运行结果为:

E
======================================================================
ERROR: test_first_last_name (__main__.NameTestCase)
测试能否正确处理样例姓名
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/(老规矩,文件路径手动打码)/day_18/test_name_function.py", line 9, in test_first_last_name
    formatted_name = get_formatted_name('python', 'java')
TypeError: get_formatted_name() missing 1 required positional argument: 'last'

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

第一行的E指出测试用例中有一个单元测试导致了错误,然后具体指出是哪一个单元测试。后面就是我们在学习异常时的老朋友——traceback,异常报告指出引发异常的原因。“Ran 1 test in 0.000s”还是python运行一个测试所耗的时间,最后一行的“FAILED (errors=1)”则表明整个测试用例都未通过,因为运行该测试用例时发生了一个错误。
测试未通过就说明测试的那部分代码是错误的,这时候千万不要想着去修改测试,而是根据错误报告调试不能通过测试的代码,现在我们来修正 get_formatted_function() 函数。

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

这里我们不要想着把函数改回第二点中的例子,因为中间名是常态,我们应该处理好这个问题,这里使用的方法是运用默认值将middle形参变为可选的。保存修改,重新运行 test_name_function.py 文件,运行结果为:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

测试用例通过,函数编写成功!

4、添加新测试
一个测试类一般都不止一个方法,因为我们我们需要综合考虑函数的所有输入情况,下面我们来完善一下test_name_function.py 文件:

import unittest
from name_function import get_formatted_name

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

	def test_first_last_name(self):
		"""测试能否正确处理样例姓名"""
		formatted_name = get_formatted_name('python', 'java')
		self.assertEqual(formatted_name, 'Python Java')

	def test_first_last_middle_name(self):
		formatted_name = get_formatted_name('python', 'java', 'ruby')
		self.assertEqual(formatted_name, 'Python Ruby Java')

unittest.main()

注意新增方法也要以 test_ 开头(否则无法在运行文件时自动运行),运行结果为:

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

测试通过,函数编写成功!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值