如何使用Mock库来模拟Django ForeignKey值?

Dan*_*aro 8 python django integration-testing python-mock

我有一个模型,我试图在不调用数据库层的情况下测试验证.我不会用文字描述,而只是发布一些示例代码.这里的问题是与Bar的ForeignKey关系,这与我正在尝试测试的内容无关,但是阻止我运行我想要的测试.

首先,myapp/models.py:

from django.core.exceptions import ValidationError
from django.db import models


class BadFooError(ValidationError):
    pass


class Bar(models.Model):
    description = models.CharField(max_length=20)


class Foo(models.Model):
    bar = models.ForeignKey(Bar)

    a_value = models.IntegerField()

    b_value = models.BooleanField()

    def clean(self):
        super(Foo, self).clean()
        if self.b_value and self.a_value > 50:
            raise BadFooError("No good")
Run Code Online (Sandbox Code Playgroud)

接下来myapp/tests.py:

from unittest import TestCase

from mock import MagicMock

from . import models


class SimpleTest(TestCase):

    def test_avalue_bvalue_validation(self):
        foo = models.Foo()
        foo.a_value = 30
        foo.b_value = True
        foo.bar = MagicMock(spec=models.Bar)
        self.assertRaises(models.BadFooError, foo.full_clean)

    def test_method_2(self):
        foo = models.Foo()
        foo.a_value = 30
        foo.b_value = True
        foo.bar = MagicMock()
        foo.__class__ = models.Bar
        self.assertRaises(models.BadFooError, foo.full_clean)

    def test_method_3(self):
        foo = models.Foo()
        foo.a_value = 30
        foo.b_value = True
        # ignore it and it will go away ...??
        self.assertRaises(models.BadFooError, foo.full_clean)
Run Code Online (Sandbox Code Playgroud)

最后,输出 python manage.py test myapp

EEE
======================================================================
ERROR: test_avalue_bvalue_validation (myapp.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "~/sandbox/myapp/tests.py", line 14, in test_avalue_bvalue_validation
    foo.bar = MagicMock(spec=models.Bar)
  File "~/dsbx/local/lib/python2.7/site-packages/django/db/models/fields/related.py", line 408, in __set__
    instance._state.db = router.db_for_write(instance.__class__, instance=value)
  File "~/dsbx/local/lib/python2.7/site-packages/django/db/utils.py", line 142, in _route_db
    return hints['instance']._state.db or DEFAULT_DB_ALIAS
  File "~/dsbx/local/lib/python2.7/site-packages/mock.py", line 658, in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute '_state'

======================================================================
ERROR: test_method_2 (myapp.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "~/sandbox/myapp/tests.py", line 21, in test_method_2
    foo.bar = MagicMock()
  File "~/dsbx/local/lib/python2.7/site-packages/django/db/models/fields/related.py", line 405, in __set__
    self.field.name, self.field.rel.to._meta.object_name))
ValueError: Cannot assign "<MagicMock id='31914832'>": "Foo.bar" must be a "Bar" instance.

======================================================================
ERROR: test_method_3 (myapp.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "~/sandbox/myapp/tests.py", line 29, in test_method_3
    self.assertRaises(models.BadFooError, foo.full_clean)
  File "/usr/lib/python2.7/unittest/case.py", line 471, in assertRaises
    callableObj(*args, **kwargs)
  File "~/dsbx/local/lib/python2.7/site-packages/django/db/models/base.py", line 926, in full_clean
    raise ValidationError(errors)
ValidationError: {'bar': [u'This field cannot be null.']}

----------------------------------------------------------------------
Ran 3 tests in 0.003s

FAILED (errors=3)
Creating test database for alias 'default'...
Destroying test database for alias 'default'...
Run Code Online (Sandbox Code Playgroud)

所以我的问题是......扫管笏吗?

Dan*_*aro 0

好吧,现在我只是将断言切换为self.assertRaises(models.BadFooError, foo.clean)(区别在于foo.cleanfoo.full_clean。虽然这有效,但似乎并不理想。我想将验证作为黑匣子进行测试。