今天是2020年2月24日,多云,14~22℃
今天开始上网课了,所幸是自己看视频,可以自由安排时间。不然怕是门门翘课😀
一、测试函数
Python标准库中的 unittest 模块提供了代码测试工具。测试分为1) 单元测试:用于测试函数的某个方面;2) 测试用例:一组单元测试,测试各种情况下的行为;3) 全覆盖式测试:一整套单元测试,涵盖了各种可能的函数使用方式。
1、创建测试
创建测试首先需要导入 unittest 模块,并导入被测试的函数。之后,创建一个继承于 unittest.TestCase 的测试类,并编写测试方法。测试方法应以 test 开头,并大致描述测试的方面。
测试方法中,应调用被测试的函数,然后使用断言方法,比较得到的结果与预期结果是否一致。
例如,对下面一个函数进行测试:
#被测试函数
def get_formatted_time(hour,minute,second):
'''获取格式化时间'''
formatted_time = str(hour) + ":" + str(minute) + ":" + str(second)
return formatted_time
预期该函数应达到返回形如 hh:mm:ss 这一表达时间字符串的效果。
下面创建测试:
import unittest
from mine import get_formatted_time
class TimeTestCase(unittest.TestCase):
'''测试mine.py'''
def test_hms_time(self):
'''能否创建hh:mm:ss形式的字符串?'''
formatted_time = get_formatted_time(13,52,26)
self.assertEqual(formatted_time,"13:52:26")
unittest.main() #运行测试
'''
Output:
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
'''
分割线上的每一个点表示每一个成功通过测试的函数。在所有测试完成后,将会显示测试所用时间。若测试成功,则会出现OK。
测试类相当于一个测试用例,而类中每一个测试方法相当于一个单元测试。
2、测试失败
上例展示了测试通过时的清醒。下面观察测试失败会发生什么。
首先,将被测试函数加入日期。
def get_formatted_time(year,mouth,day,hour,minute,second):
'''获取格式化时间'''
formatted_time = str(year) + '.' + str(mouth) + '.' + str(day)
formatted_time += ' ' + str(hour) + ':' + str(minute) + ':' + str(second)
return formatted_time
测试代码不变,再次进行测试。
返回结果如下:
'''
E
======================================================================
ERROR: test_hms_time (__main__.TimeTestCase)
能否创建hh:mm:ss形式的字符串?
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 9, in test_hms_time
formatted_time = get_formatted_time(13,52,26)
TypeError: get_formatted_time() missing 3 required positional arguments: 'hour', 'minute', and 'second'
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED (errors=1)
'''
测试未通过时,分割线上方将显示E,表示有一个单元测试未通过。下面的 traceback 说明的错误的位置和类型。最后一行 FAILED 表示整个测试未通过。
3、修改并重新测试
为使函数能满足需求,修改被测试函数:
def get_formatted_time(hour,minute,second,year='',mouth='',day=''):
'''获取格式化时间'''
formatted_time = ""
if year and mouth and day:
formatted_time = str(year) + '.' + str(mouth) + '.' + str(day) + ' '
formatted_time += str(hour) + ':' + str(minute) + ':' + str(second)
return formatted_time
在测试中新增一个测试方法,测试能否得到有年月日的时间。
import unittest
from mine import get_formatted_time
class TimeTestCase(unittest.TestCase):
'''测试mine.py'''
def test_hms_time(self):
'''能否创建hh:mm:ss形式的字符串?'''
formatted_time = get_formatted_time(13,52,26)
self.assertEqual(formatted_time,"13:52:26")
def test_ymdhms_time(self):
'''能否创建yy.mm.dd hh:mm:ss形式的字符串?'''
formatted_time = get_formatted_time(14,13,51,year=2020,mouth=2,day=24)
self.assertEqual(formatted_time,"2020.2.24 14:13:51")
unittest.main() #运行测试
'''
Output:
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
'''
由此,我们可以知道现在的函数能完成预期的功能。且当函数修改后,仍能使用这一代码测试函数是否还能完成原功能。
二、测试类
上面学习了针对单个函数的测试。下面将尝试测试整个类。
1、各种断言方法
Python在 unittest 模块中提供了很多断言方法。书上列举的6个常用断言方法如下:
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_中
2、创建测试
首先编写一个简单的类。
class School():
'''保存学校的校内师生名字'''
def __init__(self,school_name):
'''初始化学校名、学生列表和教师列表'''
self.school_name = school_name
self.students = []
self.teachers = []
def print_school_name(self):
'''显示学校名'''
print(school_name)
def store_student(self,new_student):
'''录入新学生名'''
self.students.append(new_student)
def store_teacher(self,new_teacher):
'''录入新教师名'''
self.teachers.append(new_teacher)
def print_students(self):
'''显示学生名单'''
print("学生名单:\n--------")
for student in self.students:
print(student)
print("--------")
def print_teachers(self):
'''显示教师名单'''
print("教师名单:\n--------")
for teacher in self.teachers:
print(teacher)
print("--------")
def print_all(self):
'''显示学校名、学生名单和教师名单'''
self.print_school_name()
print("--------")
self.print_students()
self.print_teachers()
然后在另一文件中创建测试。
import unittest
from mine import School
class TestSchoolCase(unittest.TestCase):
'''针对School类测试'''
def test_store_single_student(self):
'''测试单个学生名能否储存'''
school = School("ZSTU")
school.store_student("Hank")
self.assertIn("Hank",school.students)
def test_store_three_teacher(self):
'''测试三个教师名能否储存'''
school = School("ZSTU")
teachers = ["Alex","Blair","Chris"]
for teacher in teachers:
school.store_teacher(teacher)
for teacher in teachers:
self.assertIn(teacher,school.teachers)
unittest.main()
'''
Output:
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
'''
测试的过程与函数十分类似。只是在测试方法中需要实例化一个对象,然后做相应测试。
3、setUp方法
在上例的测试中,每个测试方法都单独进行了一次实例化。而 setUp 方法允许我们只需创建一次实例,就可以在所有的测试方法中使用。Python将首先运行 setUp 方法,再运行以 test 开头的方法。
import unittest
from mine import School
class TestSchoolCase(unittest.TestCase):
'''针对School类测试'''
def setUp(self):
'''创建一个实例、一组学生名单和一组教师名单'''
self.school = School("ZSTU")
self.students = ["A","B","C"]
self.teachers = ["1","2","3"]
def test_store_single_student(self):
'''测试单个学生名能否储存'''
self.school.store_student(self.students[0])
self.assertIn(self.students[0],self.school.students)
def test_store_three_teacher(self):
'''测试三个教师名能否储存'''
for teacher in self.teachers:
self.school.store_teacher(teacher)
for teacher in self.teachers:
self.assertIn(teacher,self.school.teachers)
unittest.main()
'''
Output:
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
'''
若在每个测试方法中调用 school.print_all 方法,可以发现在上一个单元测试中加入的信息会被清除。
再查看地址,发现两次测试中实例的地址并不相同。
由此可以推断,setUp会在每个单元测试前都执行一次,为该单元测试单独创建一个实例,而不是所有的单元测试使用同一个实例。
至此,本人花费了半个月的时间,总算把Python的基本语法和特性了解了一下。虽然学的很浅,也没有做项目,但也算学了点东西,没有完全浪费整个寒假。接下来学校的网课也陆续开始了,重心还是放回课内的学习上。课余时间还会参照书本做一下书上的示例,还有老师安排的项目。困难与经验也尽量隔几天总结一次,发在博客上,不要浪费了半个月每天码字的习惯。希望自己能在下个学期有更多提升,