一个快速的单元测试兼容框架,用于在许多夹具上重复测试功能
项目描述
重复测试允许您编写将相同函数应用于多组参数的测试。
例如:
from repeated_test import Fixtures
class MyFixtures(Fixtures):
def _test(self, expected, *terms):
self.assertEqual(expected, sum(terms))
a = 10, 5, 5
b = 15, 7, 8
c = 42, 1, 1
结果与单元测试兼容,并在出现错误时在回溯中提供有用的上下文:
$ python -m unittest my_tests
..F
======================================================================
FAIL: test_c (my_tests.MyFixtures)
----------------------------------------------------------------------
Traceback (most recent call last):
File "my_tests.py", line 9, in MyFixtures
c = 42, 1, 1
File "my_tests.py", line 5, in _test
self.assertEqual(expected, sum(terms))
AssertionError: 42 != 2
----------------------------------------------------------------------
Ran 3 tests in 0.002s
FAILED (failures=1)
您可以使用以下方式安装它:
$ pip install --user repeated_test
帮助/问题
您可以在 gitter.im 聊天室中获得帮助。
如果您发现任何问题或有任何请求,请使用 GitHub 问题。
参考
介绍
Python 的unittest模块有助于执行各种形式的自动化测试。编写一个派生自unittest.TestCase的类并添加各种test_xyz方法。测试运行者运行这些测试,记录成功和失败的测试,并生成这些失败原因的跟踪。
有时对大量不同的输入进行一次测试是有意义的。该模块旨在提供一种有效的方法来做到这一点。
它允许您将固定装置(输入)编写为类的普通成员,并将测试函数绑定到它们。如下所示,每个夹具都会调用此测试函数。生成的类是unittest.TestCase子类,因此它与unittest和其他与 unittest 兼容的测试运行器兼容。
构建测试用例
为了生成unittest.TestCase,repeat_test要求您:
子类repeat_test.Fixtures
编写一个带有几个参数 的_test方法,使用它需要的任何unittest.TestCase方法
直接在类主体中分配固定装置,然后将其解包为_test方法的参数(如在_test(*args)中)
您可以在测试函数中使用任何unittest.TestCase方法,例如assertEqual() 等。
from repeated_test import Fixtures
class MyFixtures(Fixtures):
def _test(self, arg1, arg2, arg3):
self.assertEqual(..., ...)
Ps = 'p1', 'p2', 'p3'
# _test(*Ps) will be called, ie. _test('p1', p2', 'p3')
Qs = 'q1', 'q2', 'q3'
# _test(*Qs) will be called, ie. _test('q1', q2', 'q3')
确保您的夹具元组为您的_test方法提供正确数量的参数,除非它具有*args参数。
运行测试用例
您可以像任何其他unittest.TestCase类一样运行repeat_test测试用例:
python -m unittest
python -m unittest my_test_module
python -m unittest my_test_module.MyFixtures
# To refer to an individual test, prefix the name of the fixture with "test_"
python -m unittest my_test_module.MyFixtures.test_Ps
在官方 unittest 文档中了解更多信息。
您还可以使用与 unittest 兼容的测试运行器,例如pytest:
python -m pytest
python -m pytest my_test_module.py
python -m pytest my_test_module.py -k MyFixtures
python -m pytest my_test_module.py -k test_Ps
python -m pytest my_test_module.py::MyFixtures::test_Ps
在官方 pytest 文档中了解更多信息
传入关键字参数
您可以使用repeat_test.options传入关键字参数:
import sys
from repeated_test import Fixtures, options
class MyFixtures(Fixtures):
def _test(self, arg1, arg2, *, min_version=None, max_version=None):
...
not_using_versions = "abc", "abc"
# -> _test("abc", "abc")
using_max_version = "abc", "abc", options(max_version=(3, 9))
# -> _test("abc", "abc", max_version=(3, 9))
using_both_versions = "abc", "abc", options(min_version=(3, 8), max_version=(3, 9))
# -> _test("abc", "abc", min_version=(3, 8), max_version=(3, 9))
using_both_versions_2 = "abc", "abc", options(min_version=(3, 8)), options(max_version=(3, 9))
# Same, but by specifying options separately
如果您有多个仅在某些时候使用的选项,这将很有用。
将关键字参数传递给多个测试
如果您在多个测试中重复使用相同的关键字参数,有几种方法可以这样做:
使用@repeated_test.with_options(...)可以为类中的每个夹具指定选项。
使用with repeat_test.options(...)可以让您为with块中的每个夹具指定选项。
您可以继续在单个夹具上使用options(),并覆盖周围代码提供的值。
您可以使用repeat_test.skip_option停止提供任何参数。该参数不会提供给函数,这对于使用默认值或从测试函数无法处理的类中删除常用选项很有用
from repeated_test import Fixtures, options, with_options
@with_options(kwarg1="value from decorator")
class MyFixtures(Fixtures):
def _test(self, arg1, arg2, *, kwarg1, kwarg2="default"):
...
using_provided_values = "arg1", "arg2"
# -> _test("arg1", "arg2", kwarg1="value from decorator", kwarg2="default")
overriding_provided_values = "arg1", "arg2", options(kwarg1="kwarg1", kwarg2="kwarg2")
# -> _test("arg1", "arg2", kwarg1="kwarg1", kwarg2="kwarg2")
with options(kwarg1="value from context manager"):
using_value_from_context_manager = "arg1", "arg2"
# -> _test("arg1", "arg2", kwarg1="value from context manager", kwarg2="default")
overriding_value_from_context_manager = "arg1", "arg2", options(kwarg1="kwarg1")
# -> _test("arg1", "arg2", kwarg1="kwarg1", kwarg2="default")
with options(kwarg2="value from context manager"):
removing_value = "arg1", "arg2"
# -> _test("arg1", "arg2", kwarg1="value from decorator", kwarg2="value from context manager")
removing_value = "arg1", "arg2", options(kwarg2=skip_option)
# -> _test("arg1", "arg2", kwarg1="value from decorator", kwarg2="default")
测试关键字参数的多个值
您还可以使用@with_options_matrix 为关键字参数提供多个值。 重复测试将运行所有组合,但被覆盖的参数除外。
from repeated_test import Fixtures, options, with_options_matrix
@with_options_matrix(
spam=["spam1", "spam2"],
ham=["ham1", "ham2"],
)
class MyFixtures(Fixtures):
def _test(self, arg1, arg2, *, spam, ham):
...
using_provided_values = "arg1", "arg2"
# -> _test("arg1", "arg2", spam="spam1", ham="ham1")
# -> _test("arg1", "arg2", spam="spam1", ham="ham2")
# -> _test("arg1", "arg2", spam="spam2", ham="ham1")
# -> _test("arg1", "arg2", spam="spam2", ham="ham2")
with options(spam="spam"):
overriding_one_value = "arg1", "arg2"
# -> _test("arg1", "arg2", spam="spam", ham="ham1")
# -> _test("arg1", "arg2", spam="spam", ham="ham2")
重复测试将使用单元测试的子测试功能报告每个组合。pytest 没有内置这个功能,但是pytest-subtests插件增加了支持。
======================================================================
FAIL: test_overriding_one_value (example_options._test) (ham='ham1')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/ykaiser/repeated_test/example_options.py", line 41, in MyFixtures
overriding_one_value = "arg1", "arg2"
File "/home/ykaiser/repeated_test/example_options.py", line 32, in _test
self.fail("example failure")
AssertionError: example failure
======================================================================
FAIL: test_overriding_one_value (example_options._test) (ham='ham2')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/ykaiser/repeated_test/example_options.py", line 41, in MyFixtures
overriding_one_value = "arg1", "arg2"
File "/home/ykaiser/repeated_test/example_options.py", line 32, in _test
self.fail("example failure")
AssertionError: example failure
命名和转义
您可以随意命名您的测试元组,尽管它们可能不以 test_或_开头。它们被复制到生成的unittest.TestCase类中,并为它们创建测试方法。他们的名字是元组的名字,前缀为 test_。
以test_或_开头的成员直接复制到生成的unittest.TestCase类,而不被视为固定装置。您可以使用它在您的固定装置或您不希望被视为测试的常量中插入常规测试:
from repeated_test import Fixtures
class MyFixtures(Fixtures):
def _test(self, arg1, arg2, arg3):
self.assertEqual(..., ...)
def test_other(self):
self.assertEqual(3, 1+2)
_spam = 'spam, bacon and eggs'
# _spam won't be treated as a fixture, so _test(*_spam) won't be called
ham = _spam, _spam, _spam
如有必要,您甚至可以使用self._test(...)调用测试函数。
分离测试和夹具
您可以使用其 with_test方法将fixtures 类应用于不同的测试函数:
class MyFixtures(Fixtures):
_test = None
...
@MyFixtures.with_test
def other_test(self, arg1, arg2, arg3):
self.assertEqual(..., ...)
虽然该函数出现在任何类之外,但它将用作生成的unittest.TestCase类的方法,因此请记住它需要一个self参数。
您可以多次重用一个夹具类。
如果以这种方式指定测试函数,则可以 在夹具定义中设置_test = None 。但是,它不会被unittest发现,因此不会运行常规测试方法。完全省略_test会引发错误,以防止意外禁用测试。
将函数用作固定装置
在此方案中,在您的夹具元组中使用函数可能是相当不切实际的。如果您的夹具元组中包含一个功能,您可以使用tup装饰器:
from repeated_test import Fixtures, tup
class my_tests(Fixtures):
def _test(self, func, arg1, arg2):
self.assertEqual(..., ...)
@tup('arg1', 'arg2')
def ham():
pass
# equivalent to
def _ham():
pass
ham = _ham, 'arg1', 'arg2'
用另一个类替换 unittest.TestCase
您可以使用WithTestClass(cls)将 unittest.TestCase 替换为另一个类。
例如,如果您希望使用unittest2:
import unittest2
from repeated_test import WithTestClass
class my_tests(WithTestClass(unittest2.TestCase)):
...
项目详情
下载文件
下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。
源分布
内置分布
repeat_test -2.2.1.tar.gz 的哈希值
| 算法 | 哈希摘要 | |
|---|---|---|
| SHA256 | b55380fb082ffc913ba493c162bdaa7a2118bae9108b247b11d4ea29d74b6376 |
|
| MD5 | 47a4759584d8446c57d804e37a62e921 |
|
| 布莱克2-256 | a12dc6b8dc23518e02607f10edc8458a0da4aaed4f3bf309f0b28ebe13966d66 |
repeat_test -2.2.1-py2.py3-none-any.whl 的哈希值
| 算法 | 哈希摘要 | |
|---|---|---|
| SHA256 | f7b35a6e60e75574722bbbb2645fbc53565aa95c42fb182fa8b78c816e6ebb5a |
|
| MD5 | 0ebd24d0ee5553f31ca5f8e6b1ec5ff6 |
|
| 布莱克2-256 | 1a2faa1d27dafc1c5e58c659ae97a2c521841ffc4cb8c9d63026f7537ad0cfc4 |