如何针对大量数据测试相同的断言

发布于 2024-09-27 02:49:32 字数 416 浏览 1 评论 0原文

我正在使用 python unittest 模块来做一些测试;然而,这是非常重复的。

我有很多数据,我想一遍又一遍地进行相同的测试,检查是否正确。然而,我必须为每个人定义一个测试。

例如我想做类似的事情。我知道我可以使用生成器来完成它(在此处的上一个线程中找到它)。但是否有替代方案,甚至可能使用不同的测试模块?

任何建议都会很棒。


import unittest

class TestData(unittest.TestCase):
    def testNumbers(self):
        numbers = [0,11,222,33,44,555,6,77,8,9999]
        for i in numbers:
            self.assertEqual(i, 33)

I am using python unittest module to do a number of tests; however, it is very repetitive.

I have a lot of data that I want to run through the same test over and over, checking if correct. However, I have to define a test for every one.

For instance I want to do something similar to this. I know I could do it using a generator (found it in a previous thread here). But are there alternatives, maybe even using a different testing module?

Any suggestions would be great.


import unittest

class TestData(unittest.TestCase):
    def testNumbers(self):
        numbers = [0,11,222,33,44,555,6,77,8,9999]
        for i in numbers:
            self.assertEqual(i, 33)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

沦落红尘 2024-10-04 02:49:32

Bill Gribble 建议的解决方案的示例代码可能如下所示:

import unittest

class DataTestCase(unittest.TestCase):
    def __init__(self, number):
        unittest.TestCase.__init__(self, methodName='testOneNumber')
        self.number = number

    def testOneNumber(self):
        self.assertEqual(self.number, 33)

    def shortDescription(self):
        # We need to distinguish between instances of this test case.
        return 'DataTestCase for number %d' % self.number


def get_test_data_suite():
    numbers = [0,11,222,33,44,555,6,77,8,9999]
    return unittest.TestSuite([DataTestCase(n) for n in numbers])

if __name__ == '__main__':
    testRunner = unittest.TextTestRunner()
    testRunner.run(get_test_data_suite())

Sample code for solution suggested by Bill Gribble could look like this:

import unittest

class DataTestCase(unittest.TestCase):
    def __init__(self, number):
        unittest.TestCase.__init__(self, methodName='testOneNumber')
        self.number = number

    def testOneNumber(self):
        self.assertEqual(self.number, 33)

    def shortDescription(self):
        # We need to distinguish between instances of this test case.
        return 'DataTestCase for number %d' % self.number


def get_test_data_suite():
    numbers = [0,11,222,33,44,555,6,77,8,9999]
    return unittest.TestSuite([DataTestCase(n) for n in numbers])

if __name__ == '__main__':
    testRunner = unittest.TextTestRunner()
    testRunner.run(get_test_data_suite())
独行侠 2024-10-04 02:49:32

从 Python 3.4 开始,您可以使用 unittest.TestCase.subTest(msg=None, **params) 上下文管理器 (文档)。这将允许您通过添加一条语句来实现您想要的目的。

这是修改为使用 subTest() 的示例

import unittest

class TestData(unittest.TestCase):
    def testNumbers(self):
        numbers = [0, 11, 222, 33, 44, 555, 6, 77, 8, 9999]
        for i in numbers:
            with self.subTest(i=i):  # added statement
                self.assertEqual(i, 33)

As of Python 3.4 you can use unittest.TestCase.subTest(msg=None, **params) context manager (documentation). This will allow you to achieve what you want by adding just one statement.

Here is your example modified to use subTest()

import unittest

class TestData(unittest.TestCase):
    def testNumbers(self):
        numbers = [0, 11, 222, 33, 44, 555, 6, 77, 8, 9999]
        for i in numbers:
            with self.subTest(i=i):  # added statement
                self.assertEqual(i, 33)
玩物 2024-10-04 02:49:32

您可能需要考虑使用unittest.TestSuite类,它允许您动态构造一组将单独运行的unittest.TestCase实例。你的unittest.TestCase子类应该只定义一个测试方法,该类接受一个构造参数,传入值来测试该特定实例。

You may want to consider using the unittest.TestSuite class, which will allow you to dynamically construct a set of unittest.TestCase instances which will get run separately. Your unittest.TestCase subclass should define just one test method, with the class accepting a construction parameter passing in the value to test against for that particular instance.

空宴 2024-10-04 02:49:32

ddt 的构建是为了准确解决您所要求的单元测试[*]。

例如:

import ddt
import unittest

@ddt.ddt
class EvalTests(unittest.TestCase):

    @ddt.data(
            ('1', 1),
            ('1 == 1',  True),
            ('1 == 2',  False),
            ('1 + 2',   4),  ## This will fail
    )
    def test_eval_expressions(self, case):
        expr, exp_value = case
        self.assertEqual(eval(expr), exp_value)

当您运行它时,您会得到 4 个测试用例,而不是只有一个:

$ python -m unittest  -v  test_eval.py
test_eval_expressions_1___1___1_ (test_eval.EvalTests) ... ok
test_eval_expressions_2___1__1___True_ (test_eval.EvalTests) ... ok
test_eval_expressions_3___1__2___False_ (test_eval.EvalTests) ... ok
test_eval_expressions_4___1_2___4_ (test_eval.EvalTests) ... FAIL

======================================================================
FAIL: test_eval_expressions_4___1_2___4_ (test_eval.EvalTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python/lib/site-packages/ddt.py", line 129, in wrapper
    return func(self, *args, **kwargs)
  File "/Work/test_eval.py", line 15, in test_eval_expressions
    self.assertEqual(eval(expr), exp_value)
AssertionError: 3 != 4

----------------------------------------------------------------------
Ran 4 tests in 0.002s

FAILED (failures=1)

请注意,ddt 尝试为生成的 TC 提供名称。

使用 pip 安装它:

pip install ddt

[*] pythonic pytest 框架 (pytest.mark.parametrize) 的相同解决方案已集成到核心工具中,值得切换到 < code>pytest 只是为了这个功能。

The ddt library was built to solve exactly what you are asking for unittest[*].

For example:

import ddt
import unittest

@ddt.ddt
class EvalTests(unittest.TestCase):

    @ddt.data(
            ('1', 1),
            ('1 == 1',  True),
            ('1 == 2',  False),
            ('1 + 2',   4),  ## This will fail
    )
    def test_eval_expressions(self, case):
        expr, exp_value = case
        self.assertEqual(eval(expr), exp_value)

And when you run it, you get 4 TestCases instead of just one:

$ python -m unittest  -v  test_eval.py
test_eval_expressions_1___1___1_ (test_eval.EvalTests) ... ok
test_eval_expressions_2___1__1___True_ (test_eval.EvalTests) ... ok
test_eval_expressions_3___1__2___False_ (test_eval.EvalTests) ... ok
test_eval_expressions_4___1_2___4_ (test_eval.EvalTests) ... FAIL

======================================================================
FAIL: test_eval_expressions_4___1_2___4_ (test_eval.EvalTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python/lib/site-packages/ddt.py", line 129, in wrapper
    return func(self, *args, **kwargs)
  File "/Work/test_eval.py", line 15, in test_eval_expressions
    self.assertEqual(eval(expr), exp_value)
AssertionError: 3 != 4

----------------------------------------------------------------------
Ran 4 tests in 0.002s

FAILED (failures=1)

Notice that ddt tries to come up with names for the generated TCs.

Install it with pip:

pip install ddt

[*] The same solution for the pythonic pytest framework (pytest.mark.parametrize) is integrated into the core tool, and is worth switching to pytest just for this feature alone.

凡间太子 2024-10-04 02:49:32

在循环中运行断言的问题是,如果其中一个断言失败,您不知道哪个值导致它(在您的示例中,它将在 0 上失败,但您不知道知道这一点直到你调试)。另一方面,重复 self.assertEqual(i, 33) 是一个更糟糕的主意,因为它会引入代码重复。

我在测试中所做的就是在测试中创建一个简单的、名称简短的内部函数,并使用不同的参数调用它。因此,您的函数将如下所示:

import unittest

class TestData(unittest.TestCase):
    def testNumbers(self):
        def eq(i):
            self.assertEqual(i, 33)
        eq(0)
        eq(11)
        eq(222)
        eq(33)
        eq(44)
        eq(555)
        ... 

这样,当 0 断言失败时,您会立即在 unittest 模块打印的堆栈跟踪上看到它。

The problem with running assertions in a loop is that, if one of the assertions fails, you don't know which value caused it (in your example, it would fail on 0, but you don't know that until you debug). On the other hand, repeating self.assertEqual(i, 33) is an even worse idea, because it introduces code duplication.

What I do in my test is to create a simple, shortly-named internal function inside the test and call it with different arguments. So your function would look like this:

import unittest

class TestData(unittest.TestCase):
    def testNumbers(self):
        def eq(i):
            self.assertEqual(i, 33)
        eq(0)
        eq(11)
        eq(222)
        eq(33)
        eq(44)
        eq(555)
        ... 

This way, when the assertion fails for 0, you immediately see it on the stack trace printed by the unittest module.

月寒剑心 2024-10-04 02:49:32

在另一篇文章中我偶然发现
鼻子测试
它更适合数据驱动的测试。


class Test_data():
    def testNumbers():
        numbers = [0,11,222,33,44,555,6,77,8,9999]
        for i in numbers:
            yield checkNumber, num

def checkNumber(num):
    assert num == 33

上面的代码与我的第一篇文章的作用完全相同。
不需要导入,只需编写一个 python 类即可。

您可以通过键入以下内容来执行测试:

nosetests filename

In another post I stumbled accross
Nose Tests
It is more suited to data driven testing.


class Test_data():
    def testNumbers():
        numbers = [0,11,222,33,44,555,6,77,8,9999]
        for i in numbers:
            yield checkNumber, num

def checkNumber(num):
    assert num == 33

The above code does the exact same thing as my first post.
No imports are needed, just write a python class.

You execute the tests by typing:

nosetests filename

顾冷 2024-10-04 02:49:32

这个答案的副产品,对我来说不太有效。在我不处理大量数据的情况下,我确实需要使用不同的输入运行相同的测试。以下测试使用我想要自定义的 create_acreate_b 方法。

要求是使用相同的自定义来运行这两个测试。

class Tests(unittest.TestCase):

    def test_a_uses_b(self):
        a = create_a()
        b = create_b()
        a.b = b
        self.assertIs(b.a, a)

    def test_b_uses_a(self):
        a = create_a()
        b = create_b()
        b.a = a
        self.assertIs(a.b, b)

我自己实例化 TestSuiteTestCase 绕过测试加载程序,导致错误,因为它需要一个名为 runTest 的方法。

结果是这样的:

class Tests(unittest.TestCase):

    def __init__(self, create_a, create_b):
        super().__init__()
        self.create_b = create_b
        self.create_a = create_a

    def test_a_uses_b(self):
        a = self.create_a()
        b = self.create_b()
        a.b = b
        self.assertIs(b.a, a)

    def test_b_uses_a(self):
        a = self.create_a()
        b = self.create_b()
        b.a = a
        self.assertIs(a.b, b)


class TestPair1(Tests):
    def __init__(self):
        super().__init__(create_a1, create_b1)


class TestPair2(Tests):
    def __init__(self):
        super().__init__(create_a2, create_b2)

Spin-off of this answer, which didn't quite work for me. Where I'm not dealing with large amounts of data, I did need to run same tests with different inputs. The following tests are using create_a and create_b methods which I want to customize.

The requirement is to run both tests with the same customization.

class Tests(unittest.TestCase):

    def test_a_uses_b(self):
        a = create_a()
        b = create_b()
        a.b = b
        self.assertIs(b.a, a)

    def test_b_uses_a(self):
        a = create_a()
        b = create_b()
        b.a = a
        self.assertIs(a.b, b)

Instantiating TestSuite and TestCase myself, bypassing the test loader, resulted in an error because it expected a single method, called runTest.

The result was this:

class Tests(unittest.TestCase):

    def __init__(self, create_a, create_b):
        super().__init__()
        self.create_b = create_b
        self.create_a = create_a

    def test_a_uses_b(self):
        a = self.create_a()
        b = self.create_b()
        a.b = b
        self.assertIs(b.a, a)

    def test_b_uses_a(self):
        a = self.create_a()
        b = self.create_b()
        b.a = a
        self.assertIs(a.b, b)


class TestPair1(Tests):
    def __init__(self):
        super().__init__(create_a1, create_b1)


class TestPair2(Tests):
    def __init__(self):
        super().__init__(create_a2, create_b2)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文