使用post_save信号模拟django模型的方法

时间:2013-12-23 22:22:29

标签: django testing mocking

所以这是我想弄清楚的事情。我有一个由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)

1 个答案:

答案 0 :(得分:2)

您需要在实际使用的模块中修补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的详细说明:

  

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

     

基本原则是你可以在查找对象的位置进行修补,这不一定与定义对象的位置相同。

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