2013-05-30 15 views
6

モデルがあり、データベースレイヤを呼び出さずに検証をテストしようとしています。言葉で表現するのではなく、サンプルコードを投稿するだけです。ここでの問題は、BarとのForeignKeyの関係です。これは、私がテストしようとしているものには関係しませんが、私が望むテストを実行するのを止めてしまいます。Mockライブラリを使って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.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) 

、I私はclean()メソッドの中から呼び出すモデル上の別のメソッドにバリデーションコードを引き出します。その後、この検証コードをユニットテストすることができます。 Fooのインスタンスを作成できるように、まだ_stage = Mock()の割り当てを行う必要がありますが、少なくともDjangoへの呼び出しは最小限に抑えられます。

関連する問題