加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

python – 具有模拟数据的ModelChoiceField的UnitTest ModelForm

发布时间:2020-12-20 11:49:58 所属栏目:Python 来源:网络整理
导读:我一直在尝试为我的ModelForm编写单元测试,它有一个ModelChoiceField.我正在使用模拟数据创建Form实例. 这是我的模特: # models.pyclass Menu(models.Model): dish = models.ForeignKey(Dish,default=None) price = models.DecimalField(max_digits=7,decim
我一直在尝试为我的ModelForm编写单元测试,它有一个ModelChoiceField.我正在使用模拟数据创建Form实例.

这是我的模特:

# models.py
class Menu(models.Model):
    dish = models.ForeignKey(Dish,default=None)
    price = models.DecimalField(max_digits=7,decimal_places=2)

# forms.py
class MenuForm(forms.ModelForm):
    class Meta:
        model = Menu
        fields = ('dish','price',)

    def clean(self):
        cleaned_data = super(MenuForm,self).clean()
        price = cleaned_data.get('price',None)
        dish = cleaned_data.get('dish',None)

        # Some validation below
        if price < 70:
            self.add_error('price','Min price threshold')
            return cleaned_data

这是我的测试用例:

class MenuFormTest(TestCase):
    def test_price_threshold(self):
        mock_dish = mock.Mock(spec=Dish)
        form_data = {
            'dish': mock_dish,'price': 80,}
        form = forms.MenuForm(data=form_data)
        self.assertTrue(form.is_valid())

此操作失败,并显示以下错误:

<ul class="errorlist"><li>dish<ul class="errorlist"><li>Select a valid choice. That choice is not one of the available choices.</li></ul></li></ul>

如何避免抛出该错误. form.is_valid()应该是True.有没有办法可以修补ModelChoiceField的查询集?我试着修补form的dish field clean()方法,如下所示:

form = forms.MenuForm(data=form_data)
dish_clean_patcher = mock.patch.object(form.fields['dish'],'clean')
dish_clean_patch = dish_clean_patcher.start()
dish_clean_patch.return_value = mock_dish

self.assertTrue(form.is_valid())

然后看起来,它在将表单数据保存到_post_clean()方法中的模型实例时失败了.这是追溯:

Traceback (most recent call last):
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/mock/mock.py",line 1305,in patched
    return func(*args,**keywargs)
  File "/vagrant/myapp/tests/test_forms.py",line 51,in test_price_threshold
    self.assertFalse(form.is_valid())
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/forms.py",line 185,in is_valid
    return self.is_bound and not self.errors
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/forms.py",line 177,in errors
    self.full_clean()
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/forms.py",line 396,in full_clean
    self._post_clean()
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/models.py",line 427,in _post_clean
    self.instance = construct_instance(self,self.instance,opts.fields,construct_instance_exclude)
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/models.py",line 62,in construct_instance
    f.save_form_data(instance,cleaned_data[f.name])
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py",line 874,in save_form_data
    setattr(instance,self.name,data)
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/db/models/fields/related.py",line 632,in __set__
    instance._state.db = router.db_for_write(instance.__class__,instance=value)
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/db/utils.py",line 300,in _route_db
    if instance is not None and instance._state.db:
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/mock/mock.py",line 716,in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute '_state'

我该如何避免这一部分?我根本不想查看instance._state.db.

我正确测试表格吗?或者我应该而不是调用form.is_valid(),只需调用form.clean()方法,完全修补super(MenuForm,self).clean()方法,并检查form.errors?

解决方法

我会说调用form.is_valid()是测试表单的好方法.我不确定嘲笑模型.

Internally the form is calling get_limit_choices_to on your dish field(Django目前为您创建).

You would need to mock the dish field’s .queryset or get_limit_choices_to here,(或调用堆栈中的其他位置,使值在这里毫无意义)以某种方式实现您想要的.

或者,在测试中创建一个Dish并让Django的内部继续做他们正在做的事情会更简单.

class MenuFormTest(TestCase):
    def test_price_threshold(self):
        dish = Dish.objects.create(
            # my values here
        )
        form_data = {
            'dish': dish.id,}
        form = MenuForm(data=form_data)
        self.assertTrue(form.is_valid())

如果您真的没有使用Django的测试数据库,那么一种策略可能是模拟MenuForm.clean和MenuForm._post_clean:

class MenuFormTest(TestCase):
    def test_price_threshold(self):
        mock_dish = mock.Mock(spec=Dish)
        form_data = {
            'dish': 1,}
        form = MenuForm(data=form_data)
        form.fields['dish'].clean = lambda _: mock_dish
        form._post_clean = lambda : None
        self.assertTrue(form.is_valid())

如果你打算这样做,你需要问问自己你的目标是什么.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读