web-dev-qa-db-fra.com

AttributeError: impossible de définir l'attribut

Je travaille sur un ancien projet Django projet, quelque part il y a une classe définie comme suit;

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)

et cette classe est utilisée dans les vues comme suit

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

cette classe a été principalement créée pour qu'ils puissent l'utiliser pour effectuer des assertions dans les tests unitaires .ie, ils n'utilisent pas Django.test.Client pour tester les vues, mais plutôt ils créent une demande factice et la transmettent à view as (appelant la vue comme appelable) dans les tests comme suit

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)

Le problème est qu'à mi-chemin de la suite de tests (une suite de tests assez énorme), certains tests commencent à exploser lors de l'exécution de la

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

et la trace de pile est

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

la trace de pile complète ressemble à quelque chose

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

le test d'échec réel est

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 pour la vue du spectacle

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

Cette réponse ne répond pas aux spécificités de cette question, mais explique le problème sous-jacent. Cette exception spécifique "AttributeError: impossible de définir l'attribut" est déclenchée (voir source ) lorsque l'attribut que vous essayez de modifier est en fait une propriété qui n'a pas un setter. Si vous avez accès au code de la bibliothèque, l'ajout d'un setter résoudrait le problème.

EDIT: lien source mis à jour vers le nouvel emplacement dans le code.

42
yoniLavi

Il semble que vous n'utilisiez pas self.template dans la classe Response. Essayez comme ceci:

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

J'ai jeté un coup d'œil au code source de Django Je ne sais pas d'où proviennent les attributs template ou templates dans HttpResponse. Mais je peux vous proposer de changer votre approche de test et de migrer vers mock framework. Vous pouvez réécrire votre test comme:

@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 Décorateur remplacez votre classe Response() par une MagicMock() et passez-la à votre méthode de test en tant que variable mock_resp. Vous pouvez également utiliser patch comme gestionnaire de contexte par la construction with, mais les décorateurs sont la meilleure façon de le faire. Je ne sais pas si Response est juste une classe de stub pour les tests mais dans ce cas, vous pouvez patcher directement HttpResponce, mais cela dépend de votre code.

Vous pouvez trouver des détails sur call_argsici . Peut-être que vous devez utiliser l'attribut spec parce que Django faites une vérification de type ... mais essayez avec et sans lui (je ne suis pas un expert Django). Explorez le framework mock: il vous donnera beaucoup d'outils puissants pour faire des tests simples.

0
Michele d'Amico