pytest: testes reutilizáveis para diferentes implementações da mesma interface
Imagine que eu implementei um utilitário (talvez uma classe) chamadoBar
em um módulofoo
e escreveu os seguintes testes para ele.
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>
Agora imagine que, no futuro, espero que diferentes implementações da mesma interface sejam escritas. Gostaria que essas implementações pudessem reutilizar os testes que foram escritos para o conjunto de testes acima: As únicas coisas que precisam mudar são
A importação doImplementation
<test data set 1>
, <test data set 2>
etc.Portanto, estou procurando uma maneira de escrever os testes acima de maneira reutilizável, que permita aos autores de novas implementações da interface poder usar os testes injetando a implementação e os dados de teste neles, sem precisar modificar o arquivo contendo a especificação original dos testes.
Qual seria uma maneira boa e idiomática de fazer isso no pytest?
==================================================== ==================
==================================================== ==================
Aqui está uma versão mais unittest que (não é bonita, mas) funciona.
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:
# 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))
Qualquer pessoa (mesmo um cliente da biblioteca) escrevendo outra implementação dessa interface
pode reutilizar o conjunto de testes definido emdefine_tests.py
injetar dados de teste próprios nos testessem modificar nenhum dos arquivos originais