基于 class 的视图中的模拟表单不使用 MagicMock

Mocking form in class based view not using the MagicMock

我一直在努力模拟一个表单 class 以在基于 class 的视图中替换它的一个实例。但看起来是这样,因为表单位于 class 属性中,它发生在 before 我用我的模拟替换了表单 class。例证:

app/views.py

from app.forms import SomeForm  # For some reason, this _is_ my mock...

class SomeViewClass(View):
    form = SomeForm  # ... while this is the _real_ SomeForm

    def post(self, request):
        form = self.form(request.POST, request.FILES)

        # Hacked around with pdb here
        # (Pdb) self.form = SomeForm <-- Force the mock into the object
        # (Pdb) form = self.form(request.POST, request.FILES)
        # (Pdb) form.is_valid() is now True
        # (Pdb) continue <--- Test finishes, and asserts are OK.

        if form.is_valid():  # This fails, as I'm running the real code
            # code, code, code

app/tests/test_views.py

from mock import MagicMock, patch

from django.tests import Client, TestCase


@patch('app.views.SomeForm')
    def test_post_valid_form_should_pass(self, mocked_form_class):
        """ Replacing SomeForm in SomeViewClass to pas the is_valid test
        """
        form_instance = MagicMock(spec=SomeForm())
        form_instance.is_valid.return_value = True
        mocked_form_class.return_value = form_instance

        self.client.login(**self.credentials)
        # code, code, code

正如您在app/views.py中插入的评论中看到的那样,我强行重新加载self.form并使用pdb重新定义了变量form,这使我的测试通过了。

似乎由于某种原因,SomeViewClass 在我开始修补 SomeForm 之前 [已注册,实例化,...]。有什么想法吗?

问题是视图已经被 Django 加载并且 form 字段已经定义并指向 SomeForm production class.

正如@DanielRoseman 和@foxyblue 在他们的评论中指出的那样,可以直接修补 class 中的字段。实际上 there was already an answer for that on SO. As pointed out, it is possible to use patch.object to patch a member of a class(在我看来,这是最好的解决方案,因为它更明确,而且更不容易出现拼写错误)

测试更正:

patch

@patch('app.views.SomeView.form', autospec=SomeForm)
    def test_post_valid_form_should_pass(self, mocked_form_class):
        """ Replacing SomeForm in SomeViewClass.form to pass the is_valid test """
        mocked_form_class.is_valid.return_value = True

        self.client.login(**self.credentials)
        # code, code, code

patch.object

@patch.object(SomeView, 'form', autospec=SomeForm)
    def test_post_valid_form_should_pass(self, mocked_form_class):
        """ Replacing SomeForm in SomeViewClass.form to pass the is_valid test """
        mocked_form_class.is_valid.return_value = True

        self.client.login(**self.credentials)
        # code, code, code