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'}))
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.
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)
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_args
ici . 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.