2013-05-30 21 views
6

Tôi có một mô hình và tôi đang cố gắng kiểm tra tính hợp lệ mà không cần gọi lớp cơ sở dữ liệu. Thay vì mô tả bằng lời tôi sẽ chỉ đăng một số mã ví dụ. Vấn đề ở đây là mối quan hệ ForeignKey với Bar, không liên quan đến những gì tôi đang cố gắng thử nghiệm nhưng ngăn tôi chạy thử nghiệm mà tôi muốn.Làm thế nào để sử dụng thư viện Mock để giả lập một giá trị Django ForeignKey?

Thứ nhất, 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") 

Tiếp theo, 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) 

Cuối cùng, đầu ra của 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'... 

Vì vậy, câu hỏi của tôi là ... wat làm gì?

Trả lời

0

Vâng, hiện tại tôi đã chuyển đơn vị của mình thành self.assertRaises(models.BadFooError, foo.clean) (sự khác biệt là foo.clean thay cho foo.full_clean). Trong khi điều này hoạt động, nó không có vẻ lý tưởng. Tôi muốn kiểm tra xác nhận là một hộp đen.

4

Trong các thử nghiệm đơn vị của tôi, tôi chỉ đơn giản là gán _state đến một trường hợp Mock mới, như trong sự thay đổi nhỏ này để lần đầu tiên thử nghiệm ví dụ đơn vị của bạn:

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) 

Tuy nhiên, để kiểm tra xác nhận của bạn như một hộp đen, tôi sẽ kéo mã xác nhận vào một phương thức riêng trên mô hình của bạn mà tôi sẽ gọi từ bên trong phương thức clean(). Sau đó bạn có thể kiểm tra đơn vị mã xác nhận này một cách cụ thể. Bạn vẫn sẽ cần thực hiện nhiệm vụ _stage = Mock() để bạn có thể tạo phiên bản Foo của mình, nhưng ít nhất bạn sẽ thu nhỏ các cuộc gọi vào Django.

Các vấn đề liên quan