如何合理组织每个测试用例都有数据文件的测试用例?

7 投票
2 回答
789 浏览
提问于 2025-04-16 23:44

我正在写一个模块,这个模块需要解析HTML来提取数据,并根据这些数据创建一个对象。简单来说,我想创建一组测试用例,每个用例都是一个HTML文件和一个对应的“标准”对象文件。

当我对解析器进行修改时,我希望能运行这个测试套件,以确保每个HTML页面的解析结果都和“标准”文件一致(这其实就是一个回归测试套件)。

我知道怎么把这个写成一个单一的测试用例,方法是从某个目录加载所有的文件对,然后逐个处理它们。但是我觉得这样最终会被报告为一个单一的测试用例,要么通过,要么失败。但我希望能得到一个报告,比如说,45个页面成功解析,47个页面中。

我该怎么安排这个呢?

2 个回答

3

这个nose测试框架支持这个功能。你可以在这里找到更多信息:http://www.somethingaboutorange.com/mrl/projects/nose/

另外,你也可以看看这个链接:如何在Python中生成动态(参数化)单元测试?

以下是我会做的事情(未经测试):

files = os.listdir("/path/to/dir")

class SomeTests(unittest.TestCase):

    def _compare_files(self, file_name): 
        with open('/path/to/dir/%s-golden' % file_name, 'r') as golden:  
            with open('/path/to/dir/%s-trial' % file_name, 'r') as trial:    
                assert golden.read() == trial.read()     


def test_generator(file_name):
    def test(self):
        self._compare_files(file_name):
    return test

if __name__ == '__main__':
    for file_name in files:
        test_name = 'test_%s' % file_name
        test = test_generator(file_name)
        setattr(SomeTests, test_name, test)
    unittest.main()
4

我之前用 unittest 这个框架做过类似的事情,写了一个函数来创建并返回一个测试类。这个函数可以接收你想要的任何参数,然后根据这些参数来定制测试类。你还可以定制测试函数的 __doc__ 属性,这样在运行测试时就能显示自定义的信息。

我快速写了下面这个示例代码来说明这个过程。这个代码并没有进行实际的测试,而是使用 random 模块故意让一些测试失败,以便演示。当这些类被创建后,它们会被放入全局命名空间,这样调用 unittest.main() 时就能找到它们。根据你运行测试的方式,你可能会想对生成的类做一些不同的处理。

import os
import unittest

# Generate a test class for an individual file.
def make_test(filename):
    class TestClass(unittest.TestCase):
        def test_file(self):
            # Do the actual testing here.
            # parsed = do_my_parsing(filename)
            # golden = load_golden(filename)
            # self.assertEquals(parsed, golden, 'Parsing failed.')

            # Randomly fail some tests.
            import random
            if not random.randint(0, 10):
                self.assertEquals(0, 1, 'Parsing failed.')

        # Set the docstring so we get nice test messages.
        test_file.__doc__ = 'Test parsing of %s' % filename

    return TestClass

# Create a single file test.
Test1 = make_test('file1.html')

# Create several tests from a list.
for i in range(2, 5):
    globals()['Test%d' % i] = make_test('file%d.html' % i)

# Create them from a directory listing.
for dirname, subdirs, filenames in os.walk('tests'):
    for f in filenames:
        globals()['Test%s' % f] = make_test('%s/%s' % (dirname, f))

# If this file is being run, run all the tests.
if __name__ == '__main__':
    unittest.main()

一个示例运行:

$ python tests.py -v
Test parsing of file1.html ... ok
Test parsing of file2.html ... ok
Test parsing of file3.html ... ok
Test parsing of file4.html ... ok
Test parsing of tests/file5.html ... ok
Test parsing of tests/file6.html ... FAIL
Test parsing of tests/file7.html ... ok
Test parsing of tests/file8.html ... ok

======================================================================
FAIL: Test parsing of tests/file6.html
----------------------------------------------------------------------
Traceback (most recent call last):
  File "generic.py", line 16, in test_file
    self.assertEquals(0, 1, 'Parsing failed.')
AssertionError: Parsing failed.

----------------------------------------------------------------------
Ran 8 tests in 0.004s

FAILED (failures=1)

撰写回答