如何使用Mock库模拟Django ForeignKey值? [英] How to use Mock library to mock a Django ForeignKey value?

查看:82
本文介绍了如何使用Mock库模拟Django ForeignKey值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a model and I'm trying to test validation without invoking the database layer. Rather than describe with words I'll just post up some example code. The issue here is the ForeignKey relationship to Bar, which isn't relevant to what I'm trying to test but is stopping me from running the test that I want.

首先, 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")

接下来, 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)

最后, 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'...

所以我的问题是...干嘛呢?

So my question is... wat do?

推荐答案

在我的单元测试中,我只是将 _state 分配给新的Mock实例,就像对第一个示例单元测试所做的微小更改一样:

In my unit tests, I simply assign _state to a new Mock instance, as in this small change to your first example unit test:

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

但是,为了将您的验证作为黑匣子进行测试,我将验证代码放入模型上的一个单独方法中,该方法将从 clean()方法内部调用.然后,您可以专门对该验证代码进行单元测试.您仍然需要执行 _stage = Mock()分配,以便可以创建Foo实例,但是至少您将使对Django的调用减到最少.

However, to test your validation as a black box, I would pull the validation code into a separate method on your model which I would call from inside the clean() method. You can then unit test this validation code specifically. You will still need to do the _stage = Mock() assignment so you can create your instance of Foo, but at least you will be minimising the calls into Django.

这篇关于如何使用Mock库模拟Django ForeignKey值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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