Python-基础入门-学习笔记(7):测试代码

Python-基础入门-学习笔记(7):测试代码

一、测试函数

1.单元测试和测试用例

Python标准库中的模块unittest提供了代码测试工具。单元测试用于核实函数的某个方面没有问题;测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。

2.可通过的测试

要为函数编写测试用例,可先导入模块unittest以及要测试的函数,再创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。

#待测文件: name_function.py
def get_formatted_name(first,last):
	"""生成整洁的姓名"""
	full_name = first + ' ' + last
	return full_name.title()
#测试
import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
	"""测试name_fnction.py"""
	
	def test_first_last_name(self):
		"""能够正确地处理姓名吗?"""
		formatted_name = get_formatted_name('janis','joplin')
		self.assertEqual(formatted_name,'Janis Joplin')

unittest.main()

我们创建了一个Test类,用来包含一系列针对get_formatted_name()的单元测试,并且要继承unittest.TestCase类。
通过调用unittest的方法assertEqual(),并向它传递了两个值判断是否相等。
Python-基础入门-学习笔记(7):测试代码
第一行的句点表示有一个测试通过了,第二行表示测试消耗的时间,最后的OK表示该测试用例中的所有单元测试都通过了。

3.不能通过的测试

通过修改待测文件,使其出现错误,结果显示:
Python-基础入门-学习笔记(7):测试代码
字母E代表测试用例中有一个单元测试导致了错误,之后是NameTestCase中的test_first_last_name()导致了错误,最后显示测试失败。

测试未通过时,请检查刚对函数所做的修改,找出导致函数行为不符合预期的修改。

4.添加新的测试

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
	"""测试name_fnction.py"""
	
	def test_first_last_name(self):
		"""能够正确地处理姓名吗?"""
		formatted_name = get_formatted_name('janis','joplin')
		self.assertEqual(formatted_name,'Janis 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')

unittest.main()

方法名必须以test打头,这样它才会在我们运行test_name_function.py时自动运行。
Python-基础入门-学习笔记(7):测试代码
练习

#city_function.py
def get_formatted_information(city,country):
	"""生成一种格式"""
	full_information = city + ',' + country
	return full_information.title()
#代码正确性测试
import unittest
from city_function import get_formatted_information

class CityTestCase(unittest.TestCase):
	"""测试city_function.py"""
	
	def test_city_country(self):
		"""能够正确处理格式"""
		formatted_information = get_formatted_information('santiago','chile')
		self.assertEqual(formatted_information,'Santiago,Chile')
		
unittest.main()

下面我们增加可选择性:

#city_function.py
def get_formatted_information(city,country,population=''):
	"""生成一种格式"""
	if population:
		full_information = city + ',' + country + ',' + 'population:' + population
	else:
		full_information = city + ',' + country
	return full_information.title()
#代码正确性测试
import unittest
from city_function import get_formatted_information

class CityTestCase(unittest.TestCase):
	"""测试city_function.py"""
	
	def test_city_country(self):
		"""能够正确处理格式"""
		formatted_information = get_formatted_information('santiago','chile')
		self.assertEqual(formatted_information,'Santiago,Chile')
	
	def test_city_country_population(self):
		"""能够正确处理三个参数的格式"""
		formatted_information = get_formatted_information('santiago','chile','50000000')
		self.assertEqual(formatted_information,'Santiago,Chile,Population:50000000')
		
unittest.main()

二、测试类

1.各种断言方法

断言方法是用来检查你认为应该满足的条件是否确实满足。以下表格的断言你需要在继承unittest.TestCase的类中使用这些方法。

方法 用途
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) 核实ltem不在list中

2.一个要测试的类

对类的测试大部分要做的工作是测试类中方法的行为,但也有一些不同之处。

首先创建一个需要测试的类:

#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 result: ")
		for response in self.responses:
			print('- ' +response)

之后创建一个程序验证是否可以正常使用:

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

最后使用方法assertIn()对类进行测试。

import unittest
from survey import AnonymousSurvey

class TsetAnonymousSurvey(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)
		
unittest.main()   #让Python运行这个文件中的测试

3.方法setUp()

unittest.TestCase类包含方法setup(),让我们只需创建这些对象一次,并在每个测试方法中使用它们。如果你在TestCase类中包含了方法setup(),Python将先运行它,再运行各个以test_打头的方法。

import unittest
from survey import AnonymousSurvey

class TsetAnonymousSurvey(unittest.TestCase):
	"""针对AnonymousSurvey类的测试"""
	def setUp(self):
		"""创建一个调查对象和一组答案,供使用的测试方法使用"""
		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_responses(self):
		"""测试三个答案会被妥善地存储"""
		for response in self.responses:
			self.my_survey.store_response(response)
		for response in self.rsponses:
			self.assertIn(response,self.my_survey.responses)

我们可以在setUp()方法中创建一系列实例并设置它们的属性,再在测试方法中直接使用这些实例。比在每个方法中都创建一下方便的多。

练习

  1. 编写一个需要测试的类
class Employee():
	"""描述员工年薪"""
	
	def __init__(self,first_name,last_name,annual_salary):
		self.name = first_name + ' ' + last_name
		self.salary = annual_salary

	def describe_someone_annual_salary(self):
		"""描述某人年薪"""
		message = self.name.title()
		message += " is " + str(self.salary) + " a year." 
		print(message)
		return message
		
	def give_raise(self,number=5000):
		"""对年薪进行提高"""
		new_salary = int(self.salary) + number
		self.salary = str(new_salary)

  1. 创建一个实例进行简单的验证
from employee import Employee

#定义一个员工,并创建一个表示统计的Employee对象
my_employee = Employee('jay','chou',100000)

#显示员工信息
my_employee.describe_someone_annual_salary()

#表现良好涨工资
my_employee.give_raise(50000)

#涨工资后
my_employee.describe_someone_annual_salary()

Python-基础入门-学习笔记(7):测试代码

  1. 测试这个类
import unittest
from employee import Employee

class TsetEmployee(unittest.TestCase):
	"""针对Employee类的测试"""
	
	def setUp(self):
		"""创建一个调查对象,供使用的测试方法使用"""
		self.my_employee = Employee('jay','chou',100000)
		self.raises = [10000,20000,30000,40000]
		
	def test_give_default_raise(self):
		"""测试能否正确显示增加默认的年薪"""
		result = self.my_employee.describe_someone_annual_salary()
		self.assertEqual(result,'Jay Chou is 100000 a year.')
		
	def test_give_custom_raise(self):
		"""测试能否够正确显示增加指定的年薪"""
		self.my_employee.give_raise(self.raises[0])
		result = self.my_employee.describe_someone_annual_salary()
		self.assertEqual(result,'Jay Chou is 110000 a year.')
		
unittest.main()

最终结果确认成功:
Python-基础入门-学习笔记(7):测试代码
由于在类中添加了打印,所以最后的结果解释为:测试通过打印逗点,之后再打印信息。Python-基础入门-学习笔记(7):测试代码
如果去掉打印功能,则是大家普遍看到的测试通过状态。
Python-基础入门-学习笔记(7):测试代码