文章目录
- 第十一章 测试代码
- 1、测试函数
- 2、测试类
第十一章 测试代码
这章比较简单,也是python学习入门的最后一章,前十一章结束,python的基本语法就结束了
这本书从第十二章到第二十章就开始介绍项目案例了
1、测试函数
要学习测试,得有要测试的代码。我们之前写过一个函数,用于接受名字的两部分,返回完整的名字
def get_name(first,last):
full_name=first+' '+last
return full_name
这个函数很简单,功能呢也显而易见,他只能接受两个参数,返回结合后的名字,我们想测试这个函数执行起来是否如我们所愿,我们只需要执行一遍,输入两个参数,看一下结果就好了。
那么假设,现在我们要修改这个函数,让他也可以处理三个参数的名字
def get_name(first,last,middle=''):
if middle:
full_name=first+' '+middle+' '+last
else:
full_name=first+' '+last
return full_name
那么现在修改好了,我们要测试我们的程序是否完成了要求,就要先输入一个两个参数的案例,再输入三个参数的案例,观察结果
那么,再假设,我们现在还要对这个函数进行扩展,每次扩展我们都会添加新的功能,并且保证之前的功能不被改变。这在实际开发中是很常见的,那么经过多次扩展之后,我们每次都需要大量的测试案例来测试我们修改后的代码是否满足要求,这很繁琐。而且有大量重复性的工作,所以,为什么不交给计算机去做呢?
模块unittest
Python标准库中的模块unittest
提供了代码测试工具。
单元测试用于核实函数的某个方面没有问题;测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情形的测试。
全覆盖式测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。对于大型项目,要实现全覆盖可能很难。
通常,最初只要针对代码的重要行为编写测试即可,等项目被广泛使用时再考虑全覆盖。
import name
import unittest
class NamesTest(unittest.TestCase):
def test_first_last_name(self):
ans_name=name.get_name('bob','dylan')
self.assertEqual(ans_name,'bob dylan')
def test_first_last_middle_name(self):
ans_name=name.get_name('a','c','b')
self.assertEqual(ans_name,'a b c')
unittest.main()
上述代码就是测试get_name
函数的一个过程
我们将get_name
函数放在name.py
文件中,然后导入name模块,再导入unittest模块
我们创建了一个名为NamesTest的类,用于包含一系列针对get_name()的单元测试。这个类的命名最好易读易懂,并包含字样Test。这个类必须继承unittest.TestCase
类,这样Python才知道如何运行你编写的测试。
NamesTest里面可以包含多个方法,用于测试get_name()
的多个方面。
函数test_first_last_name
,我们期望ans_name的值为bob dylan
。为检查是否正确,我们调用unittest
的方法assertEqual()
,并向它传递ans_name
和'bob dylan'
。代码行self.assertEqual(ans_name,'bob dylan')
的意思是说:“将ans_name的值同字符串’bob dylan’进行比较,如果它们相等,就万事大吉,如果它们不相等,跟我说一声!”
代码行unittest.main()让Python运行这个文件中的测试。输出如下:
表示两个测试都通过了
随着函数的不断扩展,你可以不断扩展你的测试用例,在有了新的更新的时候,你只需要执行测试,就能知道,之前的功能是否正确
2、测试类
断言
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中 |
类的测试与函数的测试相似——你所做的大部分工作都是测试类中方法的行为,但存在一些不同之处。
还是我们之前写过的,管理匿名调查的类
class AnonymousSurvey():
"""收集匿名调查的答案"""
def __init__(self,question):
self.question=question #注备好问题
self.anss=[] #准备好存储答案
def store_ans(self,new_ans):
"""存储单份答案"""
self.anss.append(new_ans)
def show_results(self):
"""显示收集到的所有答案"""
print("Survey results:")
a=self.anss
for ans in a:
print('- '+ans)
我们再写一个使用这个类的小程序,多人参与调查,最终显示所有结果
import name
import unittest
question="what's your name?"
mytest=name.AnonymousSurvey(question)
print("Enter 'q' at any time to quit.")
while True:
ans=input('name:')
if ans=='q':
break
mytest.store_ans(ans)
mytest.show_results()
测试结果:
Enter ‘q’ at any time to quit.
name:juli
name:tone
name:dylan
name:q
Survey results:
-juli
-tone
-dylan
下面来编写一个测试,对AnonymousSurvey类的行为进行验证:如果用户面对调查问题时提供了一个答案,我们将在这个答案被存储后,使用方法assertIn()来核实它包含在答案列表中:
class TestAnonmyous(unittest.TestCase):
def test_store_ans(self):
question="what's your name?"
mytest=name.AnonymousSurvey(question)
mytest.store_ans('python')
self.assertIn('python',mytest.anss) #核实答案是否在答案列表中
unittest.main()
输出结果
上面这个,我们只有一个测试方法,假如我们有多个测试方法的话,如下
class TestAnonmyous(unittest.TestCase):
def test_store_ans(self):
question="what's your name?"
mytest=name.AnonymousSurvey(question)
mytest.store_ans('python')
self.assertIn('python',mytest.anss)
def test_ans_q(self):
question="what's your name?"
mytest=name.AnonymousSurvey(question)
mytest.store_ans('q')
self.assertIn('q',mytest.anss)
unittest.main()
你可以注意到,我们在每个方法中都创建了一个AnonymousSurvey实例,并在每个方法中都创建了答案。
前面说过,运行unittet.main()
python会运行每个test_打头的方法,但是unittest.TestCase
类还包含了一个方法setUp()
如果你在TestCase类中包含了方法setUp(),Python将先运行它,再运行各个以test_打头的方法。
这样,我们只需在setUp()
中创建这些对象一次,并在每个测试方法中使用它们。
在你编写的每个测试方法中都可使用在方法setUp()中创建的对象。
setUp() 注意,U大写
import name
import unittest
class TestAnonmyous(unittest.TestCase):
def setUp(self):
question="what's your name?"
self.mytest=name.AnonymousSurvey(question) #创建实例
self.tests=['python','q'] #创建答案列表
def test_store_ans(self):
self.mytest.store_ans(self.tests[0])
self.assertIn(self.tests[0],self.mytest.anss)
def test_ans_q(self):
self.mytest.store_ans(self.tests[1])
self.assertIn(self.tests[1],self.mytest.anss)
unittest.main()
Ran 4 tests in 0.004s
OK
方法setUp()做了两件事情:创建一个调查对象;创建一个答案列表。
存储这两样东西的变量名包含前缀self(即存储在属性中),因此可在这个类的任何地方使用。