Python learning road 10 - test code

This series is a compilation of notes for the introductory book "Python Programming: From Getting Started to Practice", which belongs to the primary content. The title sequence follows the title of the book.

This chapter mainly learns how to use the unittest module in the Python standard library to perform simple tests on the code.

foreword

As a beginner, you don't have to write tests for every project you try; however, when working on projects with a lot of work, you should test the important behavior of the functions and classes you write. That way you'll be more sure that the work you're doing won't break the rest of the project, and you'll be able to improve your existing code as much as you want. If you accidentally break the original functionality, you'll know right away, allowing you to easily fix the problem. It's much easier to take action when a test fails than to wait for a bug to appear and then fix it. Also, if you want to share your project, code with tests is more acceptable.

test function

a passable test

Here is a function that concatenates the first and last names entered by the user:

# name_function.py
def get_formatted_name(first, last):
    """返回一个整洁的完整姓名"""
    full_name = first + " " + last
    return full_name.title()


if __name__ == '__main__':
    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)
        print("\nNeatly formatted name: " + formatted_name + ".")

Of course, you can also put the code below the if statement in a separate file, and bring in the get_formatted_name()function at the beginning of the file.

Correct if __name__ == "__main__"addition:

In Python, a module is an object, and all modules have a built-in attribute __name__. When the module is imported, the __name__attribute of the module will be set to the module name. When the module is run directly, or the file is run directly, the attribute The default value will be used "__main__", which can be summed up in a classic sentence:

Make a script both importable and executable.

ifThe code below the statement is equivalent to the test of the above function, but such a test requires us to input data each time, and judge whether the code is working properly according to the result. If the code is a little more, a little more complicated, such a test method It will be tedious, so, we use unittestmodules to test the code.

# 代码test_name_function.py:
import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
    """测试name_function.py"""

    def test_first_last_name(self):
        """能够正确地处理像Janis Joplin这样的名字吗?"""
        formatted_name = get_formatted_name("janis", "joplin")
        self.assertEqual(formatted_name, "Janis Joplin")

unittest.main()

# 结果:
.     # 这里有个实心句点
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Here are two concepts first:

Unit tests : used to verify that the function is OK in some way

Test case : A set of unit tests that together verify that a function behaves as required in various situations.

That is, you can think of the above code test_first_last_nameas unit tests, but NamesTestCaseas test cases.

Generally, test files are placed in a separate folder, or all tests can be placed in one file.

To write a test case for a function, import unittestthe module and the function to be tested, create an inherited unittest.TestCaseclass, and write a series of methods to test different aspects of the function's behavior. In testing, we use assertions self.assertEqual()(not just this assertion function) to determine whether the result is the same as expected. Every test method in a test class must test_start with , otherwise it will not be considered a unit test. Finally we unittest.main()run through all the tests in this file. When the test passes, a solid period will be output in the result first, and several periods will be output to indicate that several unit tests have passed, then the number of unit tests will be output, and finally the output will be output OK.

a failing test

The foreigner's name also has a middle name, which the above code does not take into account. We demonstrate that the test fails by changing the above code to a version with a middle name:

# 代码:
def get_formatted_name(first, middle, last):
    """返回一个整洁的完整姓名"""
    full_name = first + " " + middle + " " + last
    return full_name.title()

# 其余代码均不变

# 运行上面测试代码后的结果:
E
======================================================================
ERROR: test_first_last_name (__main__.NamesTestCase)
能够正确地处理像Janis Joplin这样的名字吗?
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_name_function.py", line 10, 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)

The first line prints a letter indicating Ethat tracebacka parameter is missing. If the conditions you checked are correct, passing the test means the function behaves correctly, and failing the test means that the new code you wrote is wrong. Therefore, when the test fails, instead of modifying the test code, you lose the modification of the code you wrote.

Add new test

Below we get_formatted_name()modify the above function to automatically handle middle names and add a unit test to the test file:

# name_function.py
def get_formatted_name(first, last, middle=""):
    """返回一个整洁的完整姓名"""
    if middle:
        full_name = first + " " + middle + " " + last
    else:
        full_name = first + " " + last
    return full_name.title()

# test_name_function.py
import unittest
from chapter11 import get_formatted_name

class NamesTestCase(unittest.TestCase):
    """测试name_function.py"""

    def test_first_last_name(self):
        """能够正确地处理像Janis Joplin这样的名字吗?"""
        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()

# 结果:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

test class

The above are all tests for functions, here we start the tests for classes. Before testing, let’s introduce several commonly used assertion methods:

method use
assertEqual(a, b) Verify that a == b
assertNotEqual(a, b) verify 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 the item is not in the list

Let's create an anonymous survey class:

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

Here is the test code for the class:

# test_survey.py
import unittest
from chapter11 import AnonymousSurvey

class TestAnonymousSurvey(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.responses:
            self.assertIn(response, self.my_survey.responses)

unittest.main()

# 结果:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

The method here is setUp()equivalent to the method of an ordinary function __init__(), which is used to initialize the test class and reduce repetitive code. For example, if the method is not setUp()used, the questionvariable must be declared once in each test function, which is very troublesome and inefficient. If you include a setUp()method in your test class, Python will run it first, and then run each test_method that starts with .

At this point, the basic part of Python is roughly over, followed by the project part, which may be supplemented in the future.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325657847&siteId=291194637