2013-05-30 213 views
6

我有一個模型,我試圖在不調用數據庫層的情況下測試驗證。而不是用言語來描述,我只會發布一些示例代碼。這裏的問題是Bar的ForeignKey關係,它與我試圖測試的內容無關,但阻止我運行我想要的測試。如何使用模擬庫來模擬Django的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") 

接下來,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'... 

所以我的問題是...笏做輸出?

回答

0

那麼,現在我已經簡單地將我的斷言切換到self.assertRaises(models.BadFooError, foo.clean)(差別是foo.clean代替foo.full_clean)。雖然這工程,它似乎並不理想。我想將驗證測試爲黑盒子。

4

在我的單元測試,我只是將_state到一個新的模擬實例,如在這個小小的改變你的第一個例子單元測試:

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的調用最小化。