【问题标题】:mocking a method on django model using post_save signal使用 post_save 信号模拟 django 模型上的方法
【发布时间】:2013-12-23 22:22:29
【问题描述】:

所以这是我想要弄清楚的事情。我有一个由 post_save 触发的方法 对于这个“故事”模型。工作正常。我需要做的是弄清楚如何模拟测试,这样我就可以伪造电话并对我的回报做出断言。我想我需要以某种方式修补它,但我尝试了几种不同的方法,但没有取得多大成功。我能得到的最好的是一个对象实例,但它忽略了我传入的值。

我在测试中评论了我的困惑所在。欢迎任何帮助。

这是我的测试:

from django.test import TestCase
from django.test.client import Client
from marketing.blog.models import Post, Tag
from unittest.mock import patch, Mock

class BlogTestCase(TestCase):
    fixtures = [
        'auth-test.json',
        'blog-test.json',
    ]

    def setUp(self):
        self.client = Client()

    def test_list(self):
        # verify that we can load the list page
        r = self.client.get('/blog/')
        self.assertEqual(r.status_code, 200)
        self.assertContains(r, "<h1>The Latest from Our Blog</h1>")
        self.assertContains(r, '<a href="/blog/javascript-date-formatting/">Simple JavaScript Date Formatting</a>')
        self.assertContains(r, 'Page 1 of 2')

        # loading a page out of range should redirect to last page
        r = self.client.get('/blog/5/', follow=True)
        self.assertEqual(r.redirect_chain, [
            ('http://testserver/blog/2/', 302)
        ])
        self.assertContains(r, 'Page 2 of 2')

        # verify that unpublished posts are not displayed
        with patch('requests') as mock_requests:
            # my futile attempt at mocking.
            # creates <MagicMock> object but not able to call return_values
            mock_requests.post.return_value = mock_response = Mock()

            # this doesn't get to the magic mock object. Why?
            mock_response.status_code = 201

            p = Post.objects.get(id=5)
            p.published = False
            # post_save signal runs here and requests is called.
            # Needs to be mocked.
            p.save()

            r = self.client.get('/blog/')
            self.assertNotContains(r, '<a href="/blog/javascript-date-formatting/">Simple JavaScript Date Formatting</a>')

这是模型:

from django.db import models
from django.conf import settings
from django.db.models import signals
import requests

def update_console(sender, instance, raw, created, **kwargs):
    # ignoring raw so that test fixture data can load without
    # hitting this method.
    if not raw:
        update = instance

        json_obj = {
            'author': {
                'alias': 'the_dude',
                'token': 'the_dude'
            },
            'text': update.description,
        }

        headers = {'content-type': 'application/json'}

        path = 'http://testserver.com:80/content/add/'
        request = requests(path, 'POST',
            json_obj, headers=headers,
            )
        if request.status_code < 299:
            story_id = request.json().get('id')
            if story_id:
                # disconnect and reconnect signal so
                # we don't enter recursion-land
                signals.post_save.disconnect(
                    update_console,
                    sender = Story, )
                update.story_id = story_id
                update.save()
                signals.post_save.connect(
                    update_console,
                    sender = Story, )
        else:
            raise AttributeError('Error Saving to console, '+ request.text)

class Story(models.Model):
    """Lets tell a story"""
    story_id = models.CharField(
        blank=True,
        max_length=10,
        help_text="This maps to the id of the post"
    )
    slug = models.SlugField(
        unique=True,
        help_text="This is used in URL and in code references.",
    )
    description = models.TextField(
        help_text='2-3 short paragraphs about the story.',
    )

    def __str__(self):
        return self.short_headline

# add/update this record as a custom update in console
signals.post_save.connect(update_console, sender = Story)

【问题讨论】:

标签: django testing mocking


【解决方案1】:

您需要在实际使用的模块中修补requests,即

with patch('path.to.your.models.requests') as mock_requests:
    mock_requests.return_value.status_code = 200
    mock_requests.return_value.json.return_value = {'id': story_id'}
    ...

文档对where to patch提供了更详细的解释:

patch 通过(临时)将名称指向的对象更改为另一个对象。可以有许多名称指向任何单个对象,因此要使修补工作正常,您必须确保修补被测系统使用的名称。

基本原则是你在查找对象的地方打补丁,不一定和定义的地方是同一个地方。

在这里,您需要在 models 模块中修补名称 requests,因此需要提供其完整路径。

【讨论】:

  • 嗨 Nicholas,感谢您的回答...我已经尝试在我使用它的地方修补 requests 模块,并从中返回了一个魔术模拟对象;但由于某种原因,我无法获得我试图模拟从我的测试返回的值。例如,在将 ('my.model.requests') 修补为 mock_requests 后,我声明 mock_requests.status_code = 201。当测试运行时,我无法获得返回的状态代码,只是一个魔术模拟实例。 . 例如:(Pdb) request.status_code
  • @uxtx 当您调用request = requests(...) 时,您需要修补返回值。我已经相应地更新了我的示例。
  • 尼古拉斯,非常感谢您的澄清。成功了。
猜你喜欢
  • 1970-01-01
  • 2017-03-20
  • 1970-01-01
  • 2015-08-20
  • 2021-04-24
  • 2022-01-17
  • 2023-03-24
  • 2011-01-10
  • 2020-09-28
相关资源
最近更新 更多