AttributeError:无法设置属性

时间:2014-12-10 08:29:41

标签: python django unit-testing httpresponse

我正在开发一个遗留的django项目,在那里有一个类定义如下;

from django.http import HttpResponse

class Response(HttpResponse):
    def __init__(self, template='', calling_context='' status=None):
        self.template = template
        self.calling_context = calling_context
        HttpResponse.__init__(self, get_template(template).render(calling_context), status)

这个类在视图中使用如下

def some_view(request):
    #do some stuff
    return Response('some_template.html', RequestContext(request, {'some keys': 'some values'}))

这个类主要是为了让他们可以在单元测试中使用它来执行断言。他们没有使用django.test.Client来测试视图,而是创建了一个模拟请求并将其传递给视图(在测试中将视图称为可调用的,如下所示

def test_for_some_view(self):
    mock_request = create_a_mock_request()
    #call the view, as a function
    response = some_view(mock_request) #returns an instance of the response class above
    self.assertEquals('some_template.html', response.template)
    self.assertEquals({}, response.context)

问题在于测试套件的一半(相当大的测试套件),一些测试在执行

时开始爆炸
return Response('some_template.html', RequestContext(request, {'some keys': 'some values'}))

并且堆栈跟踪是

self.template = template
AttributeError: can't set attribute 

完整的堆栈跟踪看起来像

======================================================================
ERROR: test_should_list_all_users_for_that_specific_sales_office
 ----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/austiine/Projects/mped/console/metrics/tests/unit/views/sales_office_views_test.py",   line 106, in test_should_list_all_users_for_that_specific_sales_office
    response = show(request, sales_office_id=sales_office.id)
File "/Users/austiine/Projects/mped/console/metrics/views/sales_office_views.py", line 63, in show
    "sales_office_users": sales_office_users}))
File "/Users/austiine/Projects/mped/console/metrics/utils/response.py", line 9, in __init__
    self.template = template
    AttributeError: can't set attribute

实际的失败测试是

def test_should_list_all_users_for_that_specific_sales_office(self):
    user_company = CompanyFactory.create()
    request = self.mock_request(user_company)
    #some other stuff

    #calling the view
    response = show(request, sales_office_id=sales_office.id)
    self.assertIn(user, response.calling_context["sales_office_users"])
    self.assertNotIn(user2, response.calling_context["sales_office_users"])

节目视图的代码

def show(request, sales_office_id):
    user = request.user
    sales_office = []
    sales_office_users = []
    associated_market_names = []
    try:
        sales_office = SalesOffice.objects.get(id=sales_office_id)
        sales_office_users = User.objects.filter(userprofile__sales_office=sales_office)
        associated_market_names = Market.objects.filter(id__in=           (sales_office.associated_markets.all())).values_list("name", flat=True)
        if user.groups.all()[0].name == UserProfile.COMPANY_AO:
            associated_market_names = [market.name for market in sales_office.get_sales_office_user_specific_markets(user)]
        except:
            pass
    return Response("sales_office/show.html", RequestContext(request, {'keys': 'values'}))

3 个答案:

答案 0 :(得分:34)

这个答案并没有解决这个问题的具体细节,而是解释了潜在的问题。 当您尝试更改的属性实际上是没有设置者的source时,会引发此特定异常“AttributeError:无法设置属性”(请参阅​​property)。如果您可以访问库的代码,添加setter可以解决问题。

编辑:更新了代码中新位置的源链接。

答案 1 :(得分:0)

您似乎不在self.template课程中使用Response。试试这样:

class Response(HttpResponse):
    def __init__(self, template='', calling_context='' status=None):
        HttpResponse.__init__(self, get_template(template).render(calling_context), status)

答案 2 :(得分:0)

我看了一下django源代码我不知道templatetemplates属性来自HttpResponse。但我可以建议您更改测试方法并迁移到mock框架。你可以改写你的测试:

@patch("qualified_path_of_response_module.response.Response", spec=Response)
def test_should_list_all_users_for_that_specific_sales_office(self,mock_resp):
    user_company = CompanyFactory.create()
    request = self.mock_request(user_company)
    #some other stuff

    #calling the view
    response = show(request, sales_office_id=sales_office.id)
    self.assertTrue(mock_resp.called)
    context = mock_resp.call_args[0][2]
    self.assertIn(user, context["sales_office_users"])
    self.assertNotIn(user2, context["sales_office_users"])

@patch装饰器将Response()类替换为MagicMock(),并将其作为mock_resp变量传递给您的测试方法。您还可以使用patch作为with构造的上下文管理器,但装饰器是更简洁的方法。我不知道Response是否只是用于测试的存根类,但在这种情况下,您可以直接修补HttpResponce,但这取决于您的代码。

您可以找到有关call_args here的详细信息。也许你需要使用spec属性,因为django进行了一些类型检查......但尝试使用和不使用它(我不是django专家)。探索mock框架:它将为您提供许多强大的工具来进行简单的测试。