29

我正在为 Django 项目编写一些单元测试,我想知道是否可以(或有必要?)测试我为它编写的一些装饰器。

这是我编写的装饰器示例:

class login_required(object):

    def __init__(self, f):
        self.f = f

    def __call__(self, *args):
        request = args[0]
        if request.user and request.user.is_authenticated():
            return self.f(*args)
        return redirect('/login')
4

4 回答 4

32

简单地:

from nose.tools import assert_equal
from mock import Mock

class TestLoginRequired(object):
    def test_no_user(self):
        func = Mock()
        decorated_func = login_required(func)
        request = prepare_request_without_user()
        response = decorated_func(request)
        assert not func.called
        # assert response is redirect

    def test_bad_user(self):
        func = Mock()
        decorated_func = login_required(func)
        request = prepare_request_with_non_authenticated_user()
        response = decorated_func(request)
        assert not func.called
        # assert response is redirect

    def test_ok(self):
        func = Mock(return_value='my response')
        decorated_func = login_required(func)
        request = prepare_request_with_ok_user()
        response = decorated_func(request)
        func.assert_called_with(request)
        assert_equal(response, 'my response')

模拟库在这里提供帮助

于 2010-04-30T09:52:06.517 回答
2

对于那些寻找 django 类型装饰器测试的人来说,这就是我在自定义 django 装饰器上运行测试的方式:

公共/装饰器.py

from functools import wraps
from django.http import Http404


def condition_passes_test(test_func, fail_msg=''):
    """
    Decorator for views that checks that a condition passes the given test,
    raising a 404 if condition fails
    """

    def decorator(view_func):
        @wraps(view_func)
        def _wrapped_view(request, *args, **kwargs):
            if test_func():
                return view_func(request, *args, **kwargs)
            else:
                raise Http404(fail_msg)
        return _wrapped_view
    return decorator

测试

import django
from django.test import TestCase
from django.http import Http404
from django.http import HttpResponse
from django.test import RequestFactory

from common import decorators


class TestCommonDecorators(TestCase):

    def shortDescription(self):
        return None

    def test_condition_passes_test1(self):
        """
        Test case where we raise a 404 b/c test function returns False
        """
        def func():
            return False

        @decorators.condition_passes_test(func)
        def a_view(request):
            return HttpResponse('a response for request {}'.format(request))
        request = RequestFactory().get('/a/url')

        with self.assertRaises(django.http.response.Http404):
            a_view(request)

    def test_condition_passes_test2(self):
        """
        Test case where we get 200 b/c test function returns True
        """
        def func():
            return True

        @decorators.condition_passes_test(func)
        def a_view(request):
            return HttpResponse('a response for request {}'.format(request))

        request = RequestFactory().get('/app_health/z')
        request = a_view(request)
        self.assertEquals(request.status_code, 200)

注意:我在my_test_function返回True或返回的视图中使用它False

@method_decorator(condition_passes_test(my_test_function, fail_msg="Container view disabled"), name='dispatch')
于 2020-02-12T16:06:38.387 回答
1

像这样的装饰器可以简单地通过鸭子打字进行测试。只需向调用函数提供一个模拟对象,该对象似乎保留并充当请求,并查看您是否获得预期的行为。

当有必要使用单元测试时,我会说是非常个人化的。您提供的示例包含这样的基本代码,人们可能会说它没有必要。但是话又说回来,测试这样一个类的成本同样低。

于 2010-04-29T15:48:33.980 回答
1

Django 的 UnitTest 示例

class TestCaseExample(TestCase):
    def test_decorator(self):
        request = HttpRequest()
        # Set the required properties of your request
        function = lambda x: x
        decorator = login_required(function)
        response = decorator(request)
        self.assertRedirects(response)

一般来说,我使用的方法如下:

  1. 设置您的请求。
  2. 创建一个虚拟函数以允许装饰器魔法发生(lambda)。在这里您可以控制最终将传递给装饰器的参数数量。
  3. 根据装饰者的响应进行断言。
于 2014-03-05T22:46:25.623 回答