Python中的代码测试


学习如何使用Python模块unittest中的工具来测试代码

对函数进行测试

  • Python标准库中的模块unittest提供了代码测试工具。 单元测试 用于核实函数的某个方面没有问题; 测试用例 是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情形的测试。 全覆盖式测试 用例包含一整套单元测试,涵盖了各种可能的函数使用方式。对于大型项目,要实现全覆盖可能很难。通常,最初只要针对代码的重要行为编写测试即可,等项目被广泛使用时再考虑全覆盖。

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

    test_name_function.py

    import unittest  # 导入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()

    第3行我们创建了一个名为NamesTestCase的类,用于包含一系列针对get_formatted_name()的单元测试。你可随便给这个类命名,但最好让它看起来与要测试的函数相关,并包含字样Test。这个类必须继承unittest.TestCase类,这样Python才知道如何运行你编写的测试。

    方法命名为test_first_last_name(),因为我们要核实的是只有名和姓的姓名能否被正确地格式化。

    我们运行 test_name_function.py 时,所有以test打头的方法都将自动运行。

  • 第8行里我们使用了unittest类最有用的功能之一:一个 断言方法 。断言方法用来核实得到的结果是否与期望的结果一致。在这里,我们知道get_formatted_name()应返回这样的姓名,即名和姓的首字母为大写,且它们之间有一个空格,因此我们期望formatted_name的值为Janis Joplin 。为检查是否确实如此,我们调用unittest的方法assertEqual(),并向它传递formatted_name和’Janis Joplin’。代码行self.assertEqual(formatted_name, 'Janis Joplin')的意思是说:“将formatted_name的值同字符串’Janis Joplin’ 进行比较,如果它们相等,就万事大吉,如果它们不相等,跟我说一声!”

  • 代码行unittest.main()让Python运行这个文件中的测试。运行 test_name_function.py 时,得到的输出如下:

    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.000s
    OK

    第1行的句点表明有一个测试通过了。接下来的一行指出Python运行了一个测试,消耗的时间不到0.001秒。最后的OK表明该测试用例中的所有单元测试都通过了。

  • 而若测试不通过,会产生下面这样的输出:

    E
    ======================================================================
    ERROR: test_first_last_name (__main__.NamesTestCase)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
    File "test_name_function.py", line 8, 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.000s
    FAILED (errors=1)
    • 第1行输出只有一个字母E,它指出测试用例中有一个单元测试导致了错误。

    • 接下来,我们看到NamesTestCase中的test_first_last_name()导致了错误。测试用例包含众多单元测试时,知道哪个测试未通过至关重要。

    • 在第5行,我们看到了一个标准的traceback,它指出函数调用get_formatted_name('janis', 'joplin')有问题,因为它缺少一个必不可少的位置实参。

    • 我们还看到运行了一个单元测试。

    • 最后,还看到了一条消息,它指出整个测试用例都未通过,因为运行该测试用例时发生了 1个错误 。这条消息位于输出末尾,让你一眼就能看到——你可不希望为获悉有多少测试未通过而翻阅长长的输出。

对类进行测试

  • Python在unittest.TestCase类中提供了很多断言方法。前面说过,断言方法检查你认为应该满足的条件是否确实满足。如果该条件确实满足,你对程序行为的假设就得到了确认,你就可以确信其中没有错误。如果你认为应该满足的条件实际上并不满足,Python将引发异常。

    import unittest
    from survey import AnonymousSurvey
    
        class TestAnonmyousSurvey(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()
  • unittest Module中的断言方法

    方法 用途
    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 中

    只能在继承unittest.TestCase的类中使用这些方法。

  • 方法setUp():

    如果你在TestCase类中包含了方法setUp(),Python将先运行它,再运行各个以test_打头的方法。这样,在你编写的每个测试方法中都可使用在方法setUp()中创建的对象了。

    下面使用setUp()来创建一个调查对象和一组答案,供方法test_store_single_response()test_store_three_responses()使用:

    import unittest
    from survey 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()

    运行测试用例时,每完成一个单元测试,Python都打印一个字符:测试通过时打印一个句点;测试引发错误时打印一个E;测试导致断言失败时打印一个F。


文章作者: Bill Z
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Bill Z !
评论
评论
  目录