在Python中运行测试包中的单元测试

2 投票
1 回答
1417 浏览
提问于 2025-04-17 09:32

我正在用Python写一个解析器,专门处理一种特殊的文本文件格式。为了了解怎么组织代码,我查看了Python标准库里JSON解析器的源代码(在Python/Lib/json目录下)。

在这个json目录里,有一个tests目录,里面放了一些单元测试。我把这些JSON测试替换成了我的测试,但现在我不知道该怎么运行它们。

在这个目录里,有一个__init__.py文件,这让它成为一个模块。在这个文件里,有一段代码是用来运行测试的:

here = os.path.dirname(__file__)

def test_suite():
    suite = additional_tests()
    loader = unittest.TestLoader()
    for fn in os.listdir(here):
        if fn.startswith("test") and fn.endswith(".py"):
            modname = "json.tests." + fn[:-3]
            __import__(modname)
            module = sys.modules[modname]
            suite.addTests(loader.loadTestsFromModule(module))
    return suite

def additional_tests():
    suite = unittest.TestSuite()
    for mod in (json, json.encoder, json.decoder):
        suite.addTest(doctest.DocTestSuite(mod))
    suite.addTest(TestPyTest('test_pyjson'))
    suite.addTest(TestCTest('test_cjson'))
    return suite

def main():
    suite = test_suite()
    runner = unittest.TextTestRunner()
    runner.run(suite)

if __name__ == '__main__':
    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
    main()

我现在的问题是,这些单元测试是怎么执行的?我有点困惑,因为if __name__ == '__main__':这个条件只有在直接调用这个文件时才会成立,而不是作为模块被导入。不过,既然它在模块的__init__.py文件里,导入后应该就会执行。

在Python控制台里用import tests命令,是否会启动所有的单元测试呢?

1 个回答

6

首先:目录中的 __init__.py 文件表示这个目录是一个包,而里面所有以 .py 结尾的文件都是模块。

其次:要验证 __name__ == '__main__' 这个条件,你需要执行这个文件。所以简单的 import 是不够的。

如果你想了解更多关于包和模块结构的内容,建议你阅读官方的 Python 文档

单元测试被组织成测试套件,测试套件可以包含一个或多个测试用例,而每个测试用例又可以包含一个或多个测试。

这些测试通常是从测试用例派生的类中的方法。你可以通过定义一个 runTest() 方法来运行测试,或者定义多个以 test_* 开头的方法,这些方法会自动执行。要执行单元测试,你可以使用方便的函数 unittest.main(),这个函数基本上会根据默认规则构建一个测试套件、测试结果和测试运行器对象。

你的单元测试的执行是由测试运行器对象完成的。标准的测试运行器类是 TextTestRunner,它使用 TextTestResult 类来存储测试结果,并将结果打印到 stdout。有关 unittest.main() 的最简单用法,请参见官方单元测试文档

总结

1) 单元测试基本上是一个包含一个或多个测试用例的测试套件:

TestSuite      <---executed by--- TestRunner
   + TestCaseA                        |
       +test_a()                      |
       +test_b()             stores test-results 
           ...                      into
       +test_z()                      |
    + TestCaseB                       V
    + TestCaseC                  TestResult

2) 测试用例是 unittest.TestCase 的子类。你可以使用加载器(例如:unittest.defaultTestLoader)来创建一个测试套件,加载器基本上是一个测试套件的工厂,或者你也可以手动添加测试用例(suite.addTest(test) / suite.addTests(tests) - tests 可以是 TestCases 或者其他 TestSuites),或者结合这两种方法。

3) 要执行一个测试套件,你需要使用一个 unittest.TestRunner 对象,它会把结果保存在一个 unittest.TestResult 对象中。

通常你会使用 unittest.TextTestRunner 对象来简单地输出测试结果到标准输出。

这正是你在主程序中发生的事情:

def main():
    suite = test_suite()                #1 create a TestSuite object
    runner = unittest.TextTestRunner()  #2 create a TextTestRunner object
    runner.run(suite)                   #3 executes the TestSuite with TestSuite
                                        #  build by the function test_suite()

要执行你的测试套件,你需要运行 python __init__.py

撰写回答