web-dev-qa-db-ja.com

AttributeError:属性を設定できません

私はレガシ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'}))
13
austiine

この回答は、この質問の詳細には対応していませんが、根本的な問題を説明しています。この特定の例外「AttributeError:ca n't set attribute」は、変更しようとしている属性が実際に property である場合に発生します( source を参照)セッター。ライブラリのコードにアクセスできる場合は、セッターを追加すると問題が解決します。

編集:コード内の新しい場所へのソースリンクを更新しました。

42
yoniLavi

Responseクラスでself.templateを使用していないようです。このようにしてみてください:

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

templateまたはtemplates属性がHttpResponseのどこから来るのかわからないDjangoソースコードを見てみました。しかし、テストアプローチを変更し、 モック フレームワークに移行することを提案できます。次のようにテストを書き直すことができます。

_@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 の詳細を確認できます。 Djangoは型チェックを行うため、spec属性を使用する必要があるかもしれませんが、それを使用して、または使用しないで試してください(私はDjangoエキスパートではありません)。 mockフレームワークを調べてください。簡単なテストを行うための強力なツールがたくさんあります。

0
Michele d'Amico