An article by Rabbit C: Use python's unittest module to test classes and functions

preface

As you write functions or classes, you can also write tests for them. By testing, you can be sure that your code works as expected for a variety of inputs. Or as you add new code features to your program, you can test them to make sure they don't break what your program already does. Programmers make mistakes, so every programmer must test their code frequently to catch problems before users notice them.
In this chapter, we will describe how to use the tools in python's unittest to test the code. We write test cases to verify that a series of inputs will get the expected output. We can see what it would be like to pass the test, what it would be like to fail the test, and also know how failing the test can help improve the code. You'll learn how to test functions and classes, and you'll know how many tests to write for your project.

1. Test function

simple function test

The prerequisite for functional testing is to have code that can be tested.
Let's start by writing a simple function that takes a first and last name and returns the tidy name:

def get_name(first,last):
	name = first +" "+last
	return name.title()

Now we start to write the test code.
We can combine the knowledge learned in the previous chapters to simulate a scene where the user enters information.
When the user enters his username information, return the information he entered and tell him the defined The information is:


from name_test import get_name

print('您已成功登陆系统,请录入您的用户名.....')

while True:
	firstname = input('请录入您的姓氏:')
	lastname = input('请您继续存储您的名称:')
	
	names = get_name(firstname,lastname)
	print("您注册的用户名称为:"+ names)

After running the test, it is found that the function logic we wrote to return the name information is correct and feasible.


Unit tests and test cases

The module unittest in the Python standard library provides code testing tools. A unit test is used to verify that an aspect of a function is correct;
a test case considers the various inputs a function may receive, and includes tests for all of these situations. A full-coverage test case contains a set of unit tests that cover every possible use of the function. For large projects, full coverage can be difficult to achieve. Usually, it is enough to write tests for important behaviors of the code at first, and then consider full coverage when the project is widely used.


passable test

The syntax for creating test cases takes a while to get used to, but once the test cases are created, adding unit tests for the functions is simple.

To write test cases for a function, you first need to import the unittest module, along with the function you want to test.
Create another class that extends unittest.TestCase and write a series of methods to test different aspects of the function's behavior.

#导入单元测试
import unittest

#导入进行测试的函数
from name_test import get_name

#创建类,用于包含一系列针对测试函数的单元测试
class NamesTestCase(unittest.TestCase):
	
	#开始进行函数测试
	def test_first_last_name(self):
		name = get_name('兔','c')
		
		self.assertEqual(name,'兔 C')

unittest.main()

This unit test code is written to test the get_name() function defined in the knowledge point of the previous section.
In this code, two classes are imported, let's take a look at the running results first, and then explain the code lines.

Look at the running results, whether the name can be returned by calling the get_name() function.
insert image description here

A period on the first line indicates that a test passed. The next line indicates that python ran a test that took less than 0.001 seconds. The ok in the last line means that all the unit tests in this test case have passed.

Next, we start explaining the code lines in the unit tests line by line.

First, we import the module unittest, and the get_name() function to be tested.
Then, we created another class named NamesTestCase to contain a series of unit tests for get_name. As for the naming convention of the class, it must be related to the content of the test, and it must also include the word Test. In addition to these specifications, be sure to inherit the unittest.TestCase class so that python will know how to run the tests you write.


NamesTestCase contains only one method for testing one method of get_name() function. Let's name this method test_name_last_name(). Because what we want to verify is whether the name with only first name and last name can be formatted correctly.
When we run the file with the suffix .py of the current class, all methods starting with test will be run automatically. In this method, we call the function to be tested and store the return value to be tested. In this example, we call get_name() with the arguments 'rabbit' and 'c' and store the returned result in the name variable.


One of the most useful features of the unittest class: an assert method. Assertion methods are used to verify that the obtained result is consistent with the expected result. Here, we know that get_name() should return such a name, the concatenation of the first name and the last name, and if it is in English, it will be returned in the form of the first letter capitalized.
In order to check whether the returned content is consistent with the content we entered, we call the unittest method: assertEqual(). As for the parameters that need to be passed in this method, 1 is a variable, that is, the variable that stores the content returned by the get_name function. 2 is the content corresponding to it. That is to manually enter the content of the string and compare it with the content stored in its variable. Of course, this comparison is performed through the asertEqual() function.
Finally, if the content is equal, it will return the result as shown in the figure above, otherwise, it will tell us the error message.


fail test

What kind of test fails?
For example, in the above example, the get_name function we defined can only receive two parameters, but not all names have two characters, what if there are three characters?

Next, let's test the name of the three characters, and call the unittest.assertEqual() function for proofreading:

import unittest
from name_test import get_name

class NamesTestCase(unittest.TestCase):
	#单元测试类中的函数一定要以test开头,才会在测试类启动时自动运行
	def test_first_last_name(self):
		name = get_name('兔','c','c')
		self.assertEqual(name,'兔CC')
		
unittest.main()

Take a look at the running results:
insert image description here
The python interpreter starts to show us an error:

You can see that it contains a lot of information because the test failed.
The first line outputs a letter E, which indicates that a unit test in the test case caused the error. And also pointed out to us where the error is.
When a test case contains many unit tests, it is crucial to know which test failed, traceback, indicating that there is a problem with the function call.
Here we know that the get_name function defined by ourselves can only receive two formal parameters, but we have passed three actual parameters.


What to do if the test fails

If the condition you're checking is correct, then the test passes, which means the function is behaving correctly.
And if the test fails, it means that there is something wrong with the code we wrote. Therefore, when the test fails, don't modify the test, but fix the code that caused the test to fail: check the changes you just made to the function to find out what caused the function behavior Unexpected modifications.

Now, all we have to do is modify the get_name() function:

def get_name(first,last,middle =''):
	if middle:
		name = first + ' ' + middle + ' ' + last
	else:
		name = first + ' ' + last
	return name.title()

After changing the get_name function, we are executing the unit test just now:
insert image description here
as you can see, the current test result has passed.


2. Test class

In the first half, we wrote test cases for functions. Next, we'll touch on tests for classes. Classes are used in many programs, so it's helpful to be able to prove that your classes work correctly. If the tests for the class pass, you can be confident that improvements to the class haven't accidentally broken its original behavior.

various assertion methods

Python provides many assertion methods in the unittest module.
As we discussed earlier, the assertion method checks whether a condition that it thinks should be true is actually true. If this condition is met, your assumptions about the behavior of the program are confirmed, and you can be confident that there is nothing wrong with it. Python will raise an exception if a condition you think should be met is not actually met.

Let's list six commonly used assertion methods:

Use the try method to verify that the returned value is equal to or not equal to the expected value, that the returned value is True or False, that the returned value is in the list or not in the list. You can only use these methods in classes that inherit unittest.TestCase.

method use
assertEqual(a,b) verify that a == b
assertNotEqual(a,b) Verify that a != b
assertTrue(x) Verify that x is True
assertFalse(x) Verify that x is False
assertIn(item,list) Verify that the item is in the list
assertNotIn(item,list) Verify that item is not in the list

test a class

Testing a class is similar to testing a function - most of what you do is test the behavior of the methods in the class, but there are a few differences, let's write a class to test.

A class to help manage anonymous surveys:

class AnonymousSurvey():
	"""收集匿名调查问卷的答案"""
	
	#初始化方法:存储一个问题,并为存储答案做准备
	def __init__(self,question):
		self.question = question
		#空列表用于存储收集到的答案
		self.response = []
		
	#调查问题的方法	
	def show_question(self):
		print(self.question)
		
	#添加新答案的方法
	def store_response(self,new_response):
		self.response.append(new_response)
	
	#显示收集到的所有答案
	def show_results(self):
		for response in self.response:
			print('-调查结果: '+response)

We use the AnonymousSurvey class we just defined to describe the collection of answers to anonymous surveys.
Among them:
__init__ The initialization method is used to collect a question and store the collected answers in an empty list.
show_question method for investigating questions.
store_response method for adding new answers collected.
show_results method for displaying all answers collected.


To know if this class will work correctly, we need to write a class that uses it:

from survey import AnonymousSurvey

#定义一个问题
question ="可以邀请您来参与我们的程序员问卷活动吗?"
#并使用这个定义的问题创建 AnonymousSurvey对象
my_survey = AnonymousSurvey(question)

#显示问题
my_survey.show_question()
print('录入q键可终止调查活动.....')

while True:
	response = input('请问您最喜欢哪一门编程语言?')
	if response == 'q':
		break
	my_survey.store_response(response)
	
#显示调查结果:
print('感谢您对本次调查问卷活动的支持!')
my_survey.show_results()

Now, we have written the AnonymousSurvey class for anonymous survey questionnaires, and the class that uses this class. Let's take a look at the effect of use:
insert image description here

If you were to make changes to the AnonymousSurvey class now, for example to allow each user to enter multiple answers, you would inadvertently change the way individual answers are handled. To make sure that no existing behavior is broken while developing this module, write tests against this class.

Test AnonymousSurvey

Let's write a test that verifies one aspect of the behavior of the AnonymousSurvey class:
if the user provides only one answer to a survey question, that answer is properly stored. To do this, we will use the method assertIn() to verify that this answer is saved in the answers list after it has been stored:

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
	def test_store_single_language(self):
		question = '请问可以邀请你参与程序员调查问卷吗?'
		my_survey = AnonymousSurvey(question)
		my_survey.store_response('c') #进行单个答案的存储
		
		self.assertIn('c',my_survey.response)
unittest.main()

In this code, we first import the unittest module and the AnonymousSurvey class.
Next, we start to define the test class, and let the test class inherit the TestCase class under the unittest module,
and then start to define the test method in the test class. Note: the test method needs to start with test_, so that when this file is executed, the class in the test method will be executed automatically.
Similarly, we first set the question to ask, and after setting it, create an instance to store the question. Next, we call its internal storage method through the instance to store the parameters in it.
After that, call the function to verify whether the content is in it for proofreading.

Let's take a look at the test results:
insert image description here
this is good, proving that there is nothing wrong with our code. But surveys that collect only one answer are not very useful.
Let's verify that when the user provides the three answers, they are also stored properly. To do this, we need to add one more method to TestAnonymousSurvey:

def test_stroe_three_response(self):
		#设置调查问题
		question = '请问您最喜欢哪一门编程语言?'
		#创建实例,并存储问题
		my_survey = AnonymousSurvey(question)

		#存储答案
		responses =['c','java','python']
		#遍历列表,将列表元素存储到调查问卷答案列表中
		for response in responses:
			my_survey.store_response(response)
		#再次遍历列表,调用 校对是否包含内容的assertIn方法
		for response in responses:
			self.assertIn(response,my_survey.response)

This time, I wrote the line-by-line meaning of the lines of code in comments.
Let's take a look at the test results of this test function:
insert image description here

Method setUp()

In the previous test example, we created an instance of AnonymousSurvey in each test method, and created the answers in each method. The unittest.TestCase class contains a method setUp() that allows us to create these objects only once and be able to use them in each test method.

If you include the setUp() method in the TestCase class, python will run it first, and then run each method starting with test_. In this way, the object created in the method setUp() can be used in every test method you write.

Now we use setUp() to create a survey object and a set of answers for use by the methods test_store_single_response() and test_store_three_responses():

from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
	def setUp(self):
		"""创建一个对象和一组答案,供使用的测试方法使用"""
		question = '请问你最喜欢哪一门编程语言?'
		self.my_survey = AnonymousSurvey(question)
		self.response = ['c','java','python']

	def test_store_single_language(self):
			self.my_survey.store_response(self.response[0])
			self.assertIn(self.response[0],self.my_survey.response)

	def test_store_three_responses(self):
			for response in self.response:
				self.my_survey.store_response(response)
			for response in self.response:
				self.assertIn(response,self.my_survey.response)
unittest.main()

insert image description here
When testing a class you write, the method setUp() makes it easier to write test methods: you can create a series of instances and set their properties in the setUp() method, and then use the instances directly in the test method. Much easier than creating an instance and setting its properties in each test method.

Guess you like

Origin blog.csdn.net/tianlei_/article/details/129272026