问题描述
我正在尝试测试 DRF 端点,并尝试将混合动态添加到测试中,以便再次执行端点中允许的每种方法(获取、发布、放置、修补、删除)
所以,我的想法是创建一个基础测试类,如果允许,它会自动向测试端点添加一些混合。我可以创建将从这个基类继承的实际测试。
代码:
from rest_framework.test import APITestCase
class GetTestMixin:
def test_get_all(self):
response = self.client.get(self.uri)
self.assertEqual(response.status_code,status.HTTP_200_OK)
class AutoMixinMeta(type):
def __call__(cls,*args,**kwargs):
allowed_methods = ['get','post']
# cls would be the Test class,for example TestExample
# cls.__bases__ is a tuple with the classes inherited in the Test class,for example:
# (<class 'unit_tests.endpoints.base_test.RESTTestCase'>,<class 'rest_framework.test.APITestCase'>)
bases = cls.__bases__
for method in allowed_methods:
bases += (cls.method_mixins[method.lower()],)
# Create a new instance with all the mixins
cls = type(cls.__name__,bases,dict(cls.__dict__))
return type.__call__(cls,**kwargs)
class RESTTestCase(Metaclass=AutoMixinMeta):
uri = None
method_mixins = {
'post': PostTestMixin,'get': GetTestMixin,}
class TestExample(RESTTestCase,APITestCase):
uri = reverse('somemodel-list')
我原以为 test_get_all 会被执行,但事实并非如此。
混合就位。我在 TestExample
中创建了一个虚拟方法并放置了一个调试器,并检查了它,如下所示:
(Pdb) self.__class__
<class 'TestExample'>
(Pdb) self.__class__.__bases__
(<class 'RESTTestCase'>,<class 'rest_framework.test.APITestCase'>,<class 'GetTestMixin'>)
解决方法
问题在于收集待测试类的代码永远不会将该类“视为”Test 类的实例,或作为以下类的子类:从测试用例继承的类仅在实例存在时才存在已创建。
实现此目的的唯一方法是在导入时创建派生类,并将所需的动态类绑定为模块上的顶级名称。
要做到这一点,您可以取消元类,而只需将语句放在模块主体中,使用 globals()
将一个或多个新类分配给名称。或者,如果您只需要子类,而不是模块顶层,则可以将代码放在 __init_subclass__
方法中。这个方法是在创建类时调用的,而不是在实例化时调用,它应该可以工作。
from rest_framework.test import APITestCase
class GetTestMixin:
def test_get_all(self):
response = self.client.get(self.uri)
self.assertEqual(response.status_code,status.HTTP_200_OK)
class RESTTestCase():
uri = None
method_mixins = {
'post': PostTestMixin,'get': GetTestMixin,}
def __init_subclass__(cls,*args,**kw):
super.__init_subclass__(*args,**kw)
if "Dynamic" in cls.__name__:
return
allowed_methods = ['get','post']
bases = list(cls.__bases__)
for method in allowed_methods:
bases.append(cls.method_mixins[method.lower()])
# Create a new instance with all the mixins
new_cls = type(cls.__name__ + "Dynamic",bases,dict(cls.__dict__))
globals()[new_cls.__name__] = new_cls
class TestExample(RESTTestCase,APITestCase):
uri = reverse('somemodel-list')
# class TestExampleDynamic is created automatically when the `class` statement above resolves