【问题标题】:How to write a test case for this Django custom tag如何为此 Django 自定义标签编写测试用例
【发布时间】:2017-11-28 14:36:30
【问题描述】:
  • Django 版本:v1.10
  • Python:3.5(事实证明这很重要..看看答案)

我找到这个答案是为了有一个 if-else 标签来比较 Django 模板中的 request.path

https://stackoverflow.com/a/19895344/80353

from django import template
from django.template.base import Node, NodeList, TemplateSyntaxError

register = template.Library()

class IfCurrentViewNode(Node):
    child_nodelists = ('nodelist_true', 'nodelist_false')

    def __init__(self, view_name, nodelist_true, nodelist_false):
        self.view_name = view_name
        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false

    def __repr__(self):
        return "<IfCurrentViewNode>"

    def render(self, context):
        view_name = self.view_name.resolve(context, True)
        request = context['request']
        if request.resolver_match.url_name == view_name:
            return self.nodelist_true.render(context)
        return self.nodelist_false.render(context)


def do_ifcurrentview(parser, token):
    bits = token.split_contents()
    if len(bits) < 2:
        raise TemplateSyntaxError("'%s' takes at least one argument"
                                  " (path to a view)" % bits[0])
    view_name = parser.compile_filter(bits[1])
    nodelist_true = parser.parse(('else', 'endifcurrentview'))
    token = parser.next_token()
    if token.contents == 'else':
        nodelist_false = parser.parse(('endifcurrentview',))
        parser.delete_first_token()
    else:
        nodelist_false = NodeList()
    return IfCurrentViewNode(view_name, nodelist_true, nodelist_false)

@register.tag
def ifcurrentview(parser, token):
    """
    Outputs the contents of the block if the current view match the argument.

    Examples::

        {% ifcurrentview 'path.to.some_view' %}
            ...
        {% endifcurrentview %}

        {% ifcurrentview 'path.to.some_view' %}
            ...
        {% else %}
            ...
        {% endifcurrentview %}
    """
    return do_ifcurrentview(parser, token)

想知道是否有办法编写测试用例来覆盖此自定义代码?

我想保持我们的测试覆盖率

【问题讨论】:

    标签: python django testing


    【解决方案1】:

    如果不是全部,您应该能够使用 mock 覆盖大部分内容。例如,假设您要测试渲染功能:

    from unittest.mock import Mock
    
    def test_render_url_match(self):
        mock_request = Mock()
        matching_url_name = 'url_name'
        mock_nodelist_true, mock_nodelist_false = Mock(), Mock()
        mock_view_name = Mock()
        mock_view_name.resolve.return_value = matching_url_name
        mock_request.resolver_match.url_name = matching_url_name
        mock_context = {'request': mock_request}
    
        custom_node = IfCurrentViewNode(mock_view_name, mock_nodelist_true, mock_nodelist_false)
        custom_node.render(mock_context)
    
        # You can then test that the correct function was called:
        # you can change to `assert_called_once` if using Python 3.6
        mock_nodelist_true.render.assert_called_once_with(mock_context)
    

    通过以这种方式设置模拟,我已确保 request.resolver_match.url_name == view_name 为真,并点击此行:return self.nodelist_true.render(context)。然后,您可以设置 url 名称,使它们不匹配并覆盖错误的情况。

    然后,对于do_ifcurrentview 函数,您也可以模拟出您需要的任何部分。也许您不想让parser.compile_filter 返回您需要的东西。只需模拟它并更改返回值:

    with mock.patch('parser.compile_filter') as mock_compile_filter:
        mock_compile_filter.return_value = 'my_expected_view_name'
    

    【讨论】:

    • 抱歉,你能说得更直白一点吗?我是 django 和单元测试的新手。所以你的大部分代码是测试IfCurrentViewNode类中的render方法?
    • 对,所以这只是一个示例,说明如何使用 Mock 来更改函数的行为,而无需真正依赖完整的 django 框架。您不需要上下文,也不需要实际的 HTTP 请求,您可以传入一个模拟并指定您想要访问的代码分支。
    • 我试过你的代码,我得到了File "/usr/src/app/dashboard/tests/tags_tests.py", line 45, in test_render_url_match custom_node.render(mock_context) File "/usr/src/app/dashboard/templatetags/tags.py", line 18, in render view_name = self.view_name.resolve(context, True) AttributeError: 'str' object has no attribute 'resolve'之类的错误我认为问题是原始代码中的view_name = self.view_name.resolve(context, True)不知何故无法解决
    • 好的,这是我假设 view_name 是一个字符串的错误。要解决这个问题,您可以传入 view_name 实际上是什么的实例,或者传入另一个 Mock。使用所有这些模拟的重点是避免重新测试 django 的代码并严格测试 your 代码。我将更新我的示例以使用模拟 view_name。请不要只是逐字复制粘贴,花一些时间来研究模拟是如何工作的。它们将在您未来的单元测试中非常有用:docs.python.org/dev/library/unittest.mock.html
    • 谢谢。如果我只是复制粘贴,您的回答仍然给我一些错误。我已经让它在我的代码中工作,因此将您的答案更改为正确的答案。请放心,我现在更好地理解了 mock,因为我在 python 3.5 中克服了我自己的 unittest.mock 问题。您的 assert_once 仅在 3.6 中可用,您还忘记了它前面的 render。非常感谢。我从中学到了一些东西。
    猜你喜欢
    • 2019-01-05
    • 2019-03-19
    • 1970-01-01
    • 2010-12-13
    • 1970-01-01
    • 1970-01-01
    • 2012-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多