AttributeError: Kan attribuut niet instellen

Ik werk aan een legacy django-project, daar is ergens een klasse als volgt gedefinieerd;

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)

en deze klasse wordt als volgt gebruikt

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

Deze klasse werd voornamelijk gemaakt, zodat ze het zouden kunnen gebruiken om beweringen in de eenheidstests uit te voeren .ie die ze niet django.test.client gebruiken om de uitzichten te testen, maar eerder een schijnaanvraag creëren en geven dat om dat te bekijken als ( de weergave als een vulbaar) in de tests als volgt bellen

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)

Het probleem is dat halverwege de testsuite (nogal een enorme testsuite), sommige tests beginnen op te blazen bij het uitvoeren van de

return Response('some_template.html', RequestContext(request, {'some keys': 'some values'}))

en het stapelspoor is

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

de volledige stacktracering ziet er ongeveer zo uit

======================================================================
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

de eigenlijke onvoldoende test is

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"])

code voor de showweergave

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'}))

Antwoord 1, autoriteit 100%

Dit antwoord gaat niet in op de specifieke kenmerken van deze vraag, maar legt het onderliggende probleem uit.
Deze specifieke uitzondering “AttributeError: kan kenmerk niet instellen” is opgeheven (zie source) wanneer het kenmerk dat u probeert te wijzigen in feite een is eigenschapdie geen setter heeft. Als je toegang hebt tot de bibliotheekcode, zou het toevoegen van een setter het probleem oplossen.

EDIT: bijgewerkte bronlink naar nieuwe locatie in de code.


Antwoord 2

Het lijkt erop dat je self.templateniet gebruikt in de klasse Response. Probeer het als volgt:

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

Antwoord 3

Ik heb een kijkje genomen naar Django broncode Ik heb geen idee waar templateof templateskenmerk afkomt van in HttpResponse. Maar ik kan u voorstellen om uw testaanpak te wijzigen en te migreren naar mock framework. U kunt uw test herschrijven zoals:

@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"])

@patchDecorator Vervang uw Response()Klasse met een MagicMock()en geef het door op uw testmethode als mock_respVariabele. U kunt ook patchgebruiken als contextbeheerder met withconstruct, maar decorateurs zijn de schonere manier om het te doen. Ik weet niet of Responseslechts een stubklasse is voor testen, maar in dat geval kunt u rechtstreeks delen HttpResponce, maar het is afhankelijk van uw code.

U vindt details over call_argshier . Misschien moet u speckenmerk gebruiken omdat Django wat typen controleert … maar probeer met en zonder het (ik ben geen Django-expert). Ontdek mockFramework: het geeft veel krachtige hulpmiddelen om eenvoudige tests te maken.

Other episodes