jac*_*acg 13 python unit-testing pytest
想象一下,我已经实现了一个Bar在模块中调用的实用程序(可能是一个类)foo,并为它编写了以下测试.
test_foo.py:
from foo import Bar as Implementation
from pytest import mark
@mark.parametrize(<args>, <test data set 1>)
def test_one(<args>):
<do something with Implementation and args>
@mark.parametrize(<args>, <test data set 2>)
def test_two(<args>):
<do something else with Implementation and args>
<more such tests>
Run Code Online (Sandbox Code Playgroud)
现在想象一下,在未来我希望能够编写相同接口的不同实现.我希望这些实现能够重用为上述测试套件编写的测试:唯一需要改变的是
Implementation<test data set 1>,<test data set 2>等等.所以我正在寻找一种以可重用的方式编写上述测试的方法,这将允许接口的新实现的作者能够通过将实现和测试数据注入其中来使用测试,而无需修改包含测试原始规范的文件.
在pytest中这样做的好方法是什么呢?
================================================== ==================
================================================== ==================
这是一个单元测试版本(不漂亮但是)有效.
define_tests.py:
# Single, reusable definition of tests for the interface. Authors of
# new implementations of the interface merely have to provide the test
# data, as class attributes of a class which inherits
# unittest.TestCase AND this class.
class TheTests():
def test_foo(self):
# Faking pytest.mark.parametrize by looping
for args, in_, out in self.test_foo_data:
self.assertEqual(self.Implementation(*args).foo(in_),
out)
def test_bar(self):
# Faking pytest.mark.parametrize by looping
for args, in_, out in self.test_bar_data:
self.assertEqual(self.Implementation(*args).bar(in_),
out)
Run Code Online (Sandbox Code Playgroud)
v1.py:
# One implementation of the interface
class Implementation:
def __init__(self, a,b):
self.n = a+b
def foo(self, n):
return self.n + n
def bar(self, n):
return self.n - n
Run Code Online (Sandbox Code Playgroud)
v1_test.py:
# Test for one implementation of the interface
from v1 import Implementation
from define_tests import TheTests
from unittest import TestCase
# Hook into testing framework by inheriting unittest.TestCase and reuse
# the tests which *each and every* implementation of the interface must
# pass, by inheritance from define_tests.TheTests
class FooTests(TestCase, TheTests):
Implementation = Implementation
test_foo_data = (((1,2), 3, 6),
((4,5), 6, 15))
test_bar_data = (((1,2), 3, 0),
((4,5), 6, 3))
Run Code Online (Sandbox Code Playgroud)
编写该接口的另一个实现的任何人(甚至是库的客户端)
define_tests.py这是参数化测试治具的绝佳用例。
您的代码可能看起来像这样:
from foo import Bar, Baz
@pytest.fixture(params=[Bar, Baz])
def Implementation(request):
return request.param
def test_one(Implementation):
assert Implementation().frobnicate()
Run Code Online (Sandbox Code Playgroud)
这将test_one运行两次:一次在Implementation = Bar位置,一次在Implementation = Baz位置。
请注意,由于Implementation只是一个固定装置,因此您可以更改其范围,或进行更多设置(可能实例化该类,或者以某种方式对其进行配置)。
如果与pytest.mark.parametrize装饰器一起使用,则pytest将生成所有排列。例如,假设上面的代码,并且此代码在这里:
@pytest.mark.parametrize('thing', [1, 2])
def test_two(Implementation, thing):
assert Implementation(thing).foo == thing
Run Code Online (Sandbox Code Playgroud)
test_two 将使用以下配置运行四次: