每个输入文件单独的测试用例?

时间:2017-08-18 08:05:58

标签: python unit-testing testing

大多数测试框架都假设“1 test = 1 Python方法/函数”, 并考虑在没有执行函数时传递的测试 提出断言。

我正在测试类似编译器的程序(一个读取*.foo的程序 文件和处理它们的内容),我想对许多输入(*.foo)文件执行相同的测试。 IOW,我的测试看起来像:

class Test(unittest.TestCase):
    def one_file(self, filename):
        # do the actual test

    def list_testcases(self):
        # essentially os.listdir('tests/') and filter *.foo files.

    def test_all(self):
        for f in self.list_testcases():
            one_file(f)

我目前的代码使用 来自unittest Python的标准库,即one_file使用self.assert...(...) 用于检查测试是否通过的陈述。

这是有效的,因为我确实得到了一个成功/失败的程序 当我的代码没问题/错误,但我失去了很多优点 测试框架:

  • 我没有得到像Y测试中的“X失败”那样的相关报道 传递/失败测试列表。 (我打算使用这样的系统 不仅要测试我自己的发展,还要评估学生的代码 作为老师,所以报告对我来说很重要)

  • 我没有获得测试独立性。第二次测试是在 第一个留下的环境,依此类推。第一次失败停止 测试套件:失败后的测试套件根本没有运行。

  • 我觉得我在滥用我的测试框架:只有 一个测试功能,所以自动测试发现unittest声音 例如,矫枉过正。相同的代码可以(应该?)写入 简单的Python,基本为assert

一个明显的选择是将我的代码更改为

class Test(unittest.TestCase):
    def one_file(self, filename):
        # do the actual test

    def test_file1(self):
        one_file("first-testcase.foo")

    def test_file2(self):
        one_file("second-testcase.foo")

然后我获得了unittest的所有优点,但是:

  • 要写的代码要多得多。

  • 很容易“忘记”一个测试用例,即创建一个测试文件 tests/并忘记将其添加到Python测试中。

我可以想象一个解决方案,我会动态地为每个测试用例生成一个方法(沿setattr(self, 'test_file' + str(n), ...)行),为第二个解决方案生成代码,而不必手动编写。但对于一个似乎并不那么复杂的用例来说,这听起来有点过分。

我怎样才能充分利用两者,即 自动测试用例发现(列出tests/*.foo个文件),测试 独立和适当的报道?

2 个答案:

答案 0 :(得分:3)

如果您可以使用pytest作为测试运行员,那么使用parametrize decorator实际上非常简单:

import pytest, glob

all_files = glob.glob('some/path/*.foo')

@pytest.mark.parametrize('filename', all_files)
def test_one_file(filename):
    # do the actual test

这也会自动为测试命名,以便您可以看到哪些文件失败了:

$ py.test
================================== test session starts ===================================
platform darwin -- Python 3.6.1, pytest-3.1.3, py-1.4.34, pluggy-0.4.0
[...]
======================================== FAILURES ========================================
_____________________________ test_one_file[some/path/a.foo] _____________________________

filename = 'some/path/a.foo'

    @pytest.mark.parametrize('filename', all_files)
    def test_one_file(filename):
>      assert False
E      assert False

test_it.py:7: AssertionError
_____________________________ test_one_file[some/path/b.foo] _____________________________

filename = 'some/path/b.foo'

    @pytest.mark.parametrize('filename', all_files)
    def test_one_file(filename):
[...]

答案 1 :(得分:0)

这是一个解决方案,虽然它可能被认为不是很漂亮......想法是动态创建新函数,将它们添加到测试类,并使用函数名作为参数(例如,文件名):

# import
import unittest

# test class
class Test(unittest.TestCase):

    # example test case
    def test_default(self):
        print('test_default')
        self.assertEqual(2,2)

# set string for creating new function    
func_string="""def test(cls):

        # get function name and use it to pass information
        filename = inspect.stack()[0][3]

        # print function name for demonstration purposes
        print(filename)

        # dummy test for demonstration purposes
        cls.assertEqual(type(filename),str)"""

# add new test for each item in list
for f in ['test_bla','test_blu','test_bli']:

    # set name of new function
    name=func_string.replace('test',f)

    # create new function
    exec(name)

    # add new function to test class
    setattr(Test, f, eval(f))

if __name__ == "__main__":
    unittest.main()

这正确运行所有四个测试并返回:

> test_bla
> test_bli
> test_blu
> test_default
> Ran 4 tests in 0.040s
> OK