2013-05-30 2 views
6

모델이 있는데 데이터베이스 레이어를 호출하지 않고 유효성 검사를 테스트하려고합니다. 단어로 설명하기보다는 예제 코드를 게시 할 것입니다. 여기서 문제는 바에 대한 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) 

마지막으로,

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'... 

그래서 제 질문은 ... 와트 할 수있다 python manage.py test myapp의 출력?

답변

0

음, 이제는 내 어설 션을 self.assertRaises(models.BadFooError, foo.clean) (은 foo.full_clean 인 차이 임)으로 전환했습니다. 이 으로 작동하지만 이상적이지는 않습니다. 검증을 블랙 박스로 테스트하고 싶었습니다. 나는

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) 

그러나, 블랙 박스로 검증을 테스트하는 내 단위 테스트에서

4

, 단순히 첫 번째 예를 단위 테스트에이 작은 변화로 새로운 모의 인스턴스에 _state을 할당 유효화 코드를 clean() 메서드에서 호출 할 모델의 별도 메서드로 가져옵니다. 그런 다음이 유효성 검사 코드를 단위 테스트 할 수 있습니다. Foo의 인스턴스를 만들 수 있도록 적어도 _stage = Mock() 할당을해야하지만, 적어도 Django에 대한 호출은 최소화 할 것입니다.