测试函数:
要学习测试,就必须要测试的代码,下面是一个简单的函数,他接受名和性并返回整洁的姓名:
举例:
name_function文件中的内容为:
#功能为返回以标题为形式的简洁的姓名 def get_formatted_name(first,last): full_name=f"{first}{last}" return full_name.title()
from name_function import get_formatted_name print("Enter 'q' at any time to quit") while True: first=input("\nplease 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)#调用name_function函数 print(f"\tNeatly formatted name:{formatted_name}")
输出结果如下所示:
Enter 'q' at any time to quit please give me a first name:jains please give me a last name:joplin Neatly formatted name:Jainsjoplin#首字母大写,标题形式 please give me a first name:q Process finished with exit code 0
现在假设要修改get_formatted_name,使其还能处理中间名,这样所时,要确保这个函数处理只含有名和姓的姓名的方式,为此,可在每次修改get_formatted_name后都进行测试,再运行程序,并输入像jains/joplin的姓名。
但是这样很繁琐,好在python提供了一种自动测试函数输出的高效方式,倘若对get_formatted_name()进行自动测试,就能始终确信当前提供测试过的姓名时,该函数都能正确工作。
单元测试和测试用例:
python标准库中的模块unittest提供了代码测试工具,但单元测试用于核实函数的某个方面没有问题。
测试用例是一组单元测试,它们一道核实函数在各种情形下的行为都符合要求,良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情形下的测试。
全覆盖的测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。对于大型项目,要进行全覆盖测试可能很难,通常,最初只要针对代码的重要行为编写测试即可,等到项目被广泛使用时在考虑全覆盖。
可通过的测试:
要为函数编写测试用例,可先导入模块unittest和要测试的函数,再创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。
举例:
使用我们上述提到的实例,返回以标题为形式的简洁的姓名
test_name_function.py
运行该文件时,所有以test_打头的方法都将自动运行
import unittest from name_function import get_formatted_name class NameTestCase(unittest.TestCase):#创建名为NameTestCase的类,用于包含一系列针对get_formatted_name的单元测试 #这个类的命名并不是唯一的,但是最好让他看起来与要测试的函数相关并包含Test的字样 #这个类必须继承unittest.TesetCase类,这样python才知道如何运行编写的程序的运行 def test_first_last_name(self):#NameTestCase类只包含test_first_last_name一个方法 # 用于测试get_formatted_name的一个方面 formatted_name=get_formatted_name("janis","joplin") self.assertEqual(formatted_name,"Janis Joplin") #断言方法:用来核实得到的结果与期望的结果一致 #assertEqual(arg1,arg2,msg(期望值)),arg1==arg2通过, False返回msg if __name__=='__main__':#if代码块是用来检验特殊变量__name__,这个变量是在程序执行时设置的。 #如果这个文件作为主程序执行,变量__name__将被设置'__main__' ''' 在这里,调用 unittest.main()来运行测试用例,但他如果不是作为主程序运行, 则变量__name__的值将不是'__main__',因此不会调用unittest.main() ''' unittest.main()
name_function.py
def get_formatted_name(first,last): full_name=f"{first}{last}" return full_name.title()
输出结果如下所示:
. #表明有一个测试通过了 ---------------------------------------------------------------------- Ran 1 test in 0.000s #指出python运行了一个测试,消耗时间不到0.001秒 OK #表明该测试用例中的所有单元测试都通过了
未通过的测试:
现在我们给get_formatted_name指定一个新的版本,使其能够处理中间名,但同时故意让该函数无法正确处理像’janis joplin‘这种,只有姓和名的名字,看看会发生什么情况:
def get_formatted_name(first, last): full_name=f"{first} {middle} {last}" return full_name.title()
输出结果:
E #测试用例中有一个单元测试导致了该错误 ====================================================================== ERROR: test_first_last_name (__main__.NameTestCase)#test_first_last_name导致了该错误 ---------------------------------------------------------------------- #下面的Traceback 指出调用get_formatted_name("janis","joplin")有问题,由于缺少一个必不可少的位置参数 Traceback (most recent call last): File "C:/Users/Lenovo/PycharmProjects/pythonProject4/test_name_function.py", line 19, 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.001s FAILED (errors=1)#整个测试未通过,因为在运行该测试用例的时候发生了一个错误
测试未通过怎么办:
如果你检查的条件没错,测试通过意味着函数的行为是对的,而测试未通过意味着编写的新代码有错,因此,测试未通过时,不要修改测试,而应修复导致测试不能通过的代码,检查刚刚对函数所做的修改,找出导致函数行为不符合预期的修改。
在上述示例中,get_formatted_name()以前只需要名和姓两个实参,但是现在要求提供名,中间名,和姓,新增的中间名参数是必不可少的,这导致get_formatted_name()的行为不符合预期。就这里而言,最佳的选择是让中间名变为可选的,这样做后,使用只有姓和名而没有中间名的姓名进行测试时,测试也能顺利通过,而且也可以接受中间名。
下面我们就对get_formatted_name()进行修改,将中间名设置为可选的,然后再次运行这个测试用例。如果通过了,就接着确认该函数能够妥善处理中间名。
具体操作如下:
def get_formatted_name(first,last, middle=''): if middle:#存在即返回含有middle的姓名 full_name=f"{first} {middle} {last}" else:#不存在即返回不含有middle的姓名 full_name=f"{first} {last}" return full_name.title()
此时,测试顺利通过。
. ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
添加新测试:
确定get_formatted_name()又能正确处理简单的姓名后,我们再编写一个测试,用于测试含中间名。为此,在NameTestCase类中再添加一个方法:
在test_name_function.py中添加新的测试:
#该测试我们想测试的目的是正确处理像"Wolfgang Amadeus Mozart"这样的姓名 def test_first_last_middle_name(self):#方法名必须以test_开头,这样我们在运行test_name_function.py时自动运行。 formatted_name = get_formatted_name("wolfgang", 'mozart', "amadeus") self.assertEqual(formatted_name, "Wolfgang Amadeus Mozart")
可以在TestCase类中使用很长的方法名,而这些方法名必须是描述性的,这样你才能看懂测试未通过时的输出,而这些方法由python自动调用,因此,你根本不用编写调用它们的代码。
输出结果如下:
两个测试都通过了!
.. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
现在我们能够无比信任get_formatted_name()不仅可以处理像’janis joplin‘这种,还可以处理像"Wolfgang Amadeus Mozart"这样的姓名了。