pytest:同一接口不同实现的可重用测试 [英] pytest: Reusable tests for different implementations of the same interface

查看:46
本文介绍了pytest:同一接口不同实现的可重用测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下,我在模块 foo 中实现了一个名为 Bar 的实用程序(可能是一个类),并为它编写了以下测试.

Imagine I have implemented a utility (maybe a class) called Bar in a module foo, and have written the following tests for it.

test_foo.py:

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>

现在想象一下,将来我希望编写相同接口的不同实现.我希望这些实现能够重用为上述测试套件编写的测试:唯一需要更改的是

Now imagine that, in the future I expect different implementations of the same interface to be written. I would like those implementations to be able to reuse the tests that were written for the above test suite: The only things that need to change are

  1. 导入Implementation
  2. <测试数据集1><测试数据集2>

所以我正在寻找一种以可重用的方式编写上述测试的方法,这将允许接口的新实现的作者能够通过将实现和测试数据注入其中来使用测试,而无需修改包含测试原始规范的文件.

So I am looking for a way to write the above tests in a reusable way, that would allow authors of new implementations of the interface to be able to use the tests by injecting the implementation and the test data into them, without having to modify the file containing the original specification of the tests.

在 pytest 中这样做的好的惯用方法是什么?

What would be a good, idiomatic way of doing this in pytest?

======================================================================

====================================================================

======================================================================

====================================================================

这是一个(不漂亮但)有效的单元测试版本.

Here is a unittest version that (isn't pretty but) works.

define_tests.py:

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)

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

v1_test.py:

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))

任何人(甚至库的客户)编写此接口的另一个实现

Anybody (even a client of the library) writing another implementation of this interface

  • 可以重用 define_tests.py
  • 中定义的测试集
  • 将自己的测试数据注入到测试中
  • 无需修改任何原始文件

推荐答案

这是 参数化测试装置.

您的代码可能如下所示:

Your code could look something like this:

from foo import Bar, Baz

@pytest.fixture(params=[Bar, Baz])
def Implementation(request):
    return request.param

def test_one(Implementation):
    assert Implementation().frobnicate()

这将使 test_one 运行两次:一次 where Implementation=Bar,一次 whereImplementation=Baz.

This would have test_one run twice: once where Implementation=Bar and once where Implementation=Baz.

请注意,由于实现只是一个固定装置,您可以更改其范围,或进行更多设置(可能实例化该类,可能以某种方式对其进行配置).

Note that since Implementation is just a fixture, you can change its scope, or do more setup (maybe instantiate the class, maybe configure it somehow).

如果与 pytest.mark.parametrize 装饰器一起使用,pytest 将生成所有排列.例如,假设上面的代码,还有这里的代码:

If used with the pytest.mark.parametrize decorator, pytest will generate all the permutations. For example, assuming the code above, and this code here:

@pytest.mark.parametrize('thing', [1, 2])
def test_two(Implementation, thing):
    assert Implementation(thing).foo == thing

test_two 将运行四次,配置如下:

test_two will run four times, with the following configurations:

  • Implementation=Bar, thing=1
  • Implementation=Bar, thing=2
  • 实施=Baz,事物=1
  • 实施=Baz,事物=2

这篇关于pytest:同一接口不同实现的可重用测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆