I am working on a legacy django project, in there somewhere there is a class defined as follows;
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)
and this class is used in views as follows
def some_view(request):
#do some stuff
return Response('some_template.html', RequestContext(request, {'some keys': 'some values'}))
this class was mainly created so that they could use it to perform assertions in the unit tests .i.e they are not using django.test.Client to test the views but rather they create a mock request and pass that to view as(calling the view as a callable) in the tests as follows
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)
The problem is that half way through the test suite(quite a huge test suite), some tests begin blowing up when executing the
return Response('some_template.html', RequestContext(request, {'some keys': 'some values'}))
and the stack trace is
self.template = template
AttributeError: can't set attribute
the full stack trace looks something like
======================================================================
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
the actual failing 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 for the show view
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'}))
This answer doesn't address the specifics of this question, but explains the underlying issue.
This specific exception "AttributeError: can't set attribute" is raised (see source) when the attribute you're attempting to change is actually a property that doesn't have a setter. If you have access to the library's code, adding a setter would solve the problem.
EDIT: updated source link to new location in the code.
Edit2:
Example of a setter:
class MAMLMetaLearner(nn.Module):
def __init__(
self,
args,
base_model,
inner_debug=False,
target_type='classification'
):
super().__init__()
self.args = args # args for experiment
self.base_model = base_model
assert base_model is args.model
self.inner_debug = inner_debug
self.target_type = target_type
#property
def lr_inner(self) -> float:
return self.args.inner_lr
#lr_inner.setter
def lr_inner(self, new_val: float):
self.args.inner_lr = new_val
It looks like you don't use self.template in Response class. Try like this:
class Response(HttpResponse):
def __init__(self, template='', calling_context='' status=None):
HttpResponse.__init__(self, get_template(template).render(calling_context), status)
I took a look to django source code I've no idea where template or templates attribute come from in HttpResponse. But I can propose to you to change your test approach and migrate to mock framework. You can rewrite your test like:
#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 decorator replace your Response() class by a MagicMock() and pass it to your test method as mock_resp variable. You can also use patch as context manager by with construct but decorators are the cleaner way to do it. I don't know if Response is just a stub class for testing but in that case you can patch directly HttpResponce, but it depends from your code.
You can find details about call_args here. Maybe you need to use spec attribute because django make some type checking... but try with and without it (I'm not a django expert). Explore mock framework: it'll give to you lot of powerful tools to make simple tests.
Related
SOLVED BY JUST RESTARTING THE SERVER (python manage.py runserver)
I want to call a model method inside another model method in models.py but i receive the error AttributeError: module 'my_website.models' has no attribute 'my_model_method'
Here is my model:
class SaleCode(models.Model):
#my fields and methods...
#the method I want to call
def generate_token(self, apply=True, length=5):
# stuff
return {
"token": token,
"self-applyed": apply
}
#save() method override
def save(self, *args, **kwargs):
if not self.pk:
#self.code is one of my fields
self.code = self.generate_token()["token"] #the line that generates the error
super(SaleCode, self).save(*args, **kwargs)
What I have tried:
(As I read somewhere) I tried to place #classmethod (and #staticmethod) over the generate_token(...) declaration and then to call it as SaleCode.generate_token():
#classmethod
def generate_token(self, apply=True, length=5):
...
self.code = SaleCode.generate_token()["token"] #the line that generates the error
I wrote the function outside the method and then called it as a normal function (it worked but it does not seem to be a "clear" way to do that.)
I really don't know why but...
I just restarted the server and it worked.. really weird but know it works.
I'm not sure what's the proper way to test functions which are used inside views/permission classes.
This is the payload of my request:
{"name": "John"}
And this is the function I want to test:
def get_name(request):
return request.data['name']
This is the view that will be using the function:
class SomeView(APIView):
def get(self, request):
name = get_name(request=request)
return Response(status=200)
How should I create a fixture to test the get_name function? I've tried this:
#pytest.fixture
def request_fixture()
factory = APIRequestFactory()
return factory.get(
path='',
data={"name": "John"},
format='json')
def test_get_name(request_fixture):
assert get_name(request=request_fixture) == "John"
But I'm getting an error:
AttributeError: 'WSGIRequest' object has no attribute data.
One workaround seems to be decoding the body attribute:
def get_name(request):
data = json.loads(request.body.decode('utf-8'))
return data['name']
But it doesn't feel like the right way to do this and I guess I'm missing something about the WSGIRequest class. Can someone explain to me how it should be tested? It would be great if I could use the same fixture to test the view too.
I don't think you need the test fixture. You aren't testing the whole view, just a helper function. You can make a request-like object very easily by adding a property to a lambda:
def test_get_name():
request = lambda: None
request.data = {"name": "John"}
assert get_name(request=request) == "John"
I wanted to get the values of self.request.get('foo') and etal everytime I create an instance of a class, so I decided to use __init__ constructor. Here's my code:
class archive(handler):
d = dict(title='',author= '')
def __init__(self):
self.d['title'] = self.request.get('title')
self.d['author'] = self.request.get('author')
class compose(handler):
def get(self):
self.render('compose.html')
def post(self):
a = archive()
My purpose is, to get rid the repetition of:
title = self.request.get('title')
author = self.request.get('author')
in every def post(self). But the problem is I get a NoneType error:
AttributeError: 'NoneType' object has no attribute 'get'
Obviously, self.request.get('title') returned None. I am just new with Python and Google Appengine.
Thank you guys.
This is how I managed to fix the problem:
class Archive(object):
d = dict(title='',author= '')
def load_d(self):
r = webapp2.get_request()
self.d['title'] = r.get('title')
self.d['author'] = r.get('author')
class Compose(Handler):
def get(self):
self.render('compose.html')
def post(self):
a = Archive()
a.load_d()
I assume you use webapp2.
Your init overrides the init of the webapp2 request handler (super). You can read in the webapp2 docs how to to this:
http://webapp-improved.appspot.com/guide/handlers.html#overriding-init
Take care when you use variables (self.variable) because you can also override variables of the request handler. You can use the request registry.
I'm writing tests for a Django application and using a attribute on my test class to store which view it's supposed to be testing, like this:
# IN TESTS.PY
class OrderTests(TestCase, ShopTest):
_VIEW = views.order
def test_gateway_answer(self):
url = 'whatever url'
request = self.request_factory(url, 'GET')
self._VIEW(request, **{'sku': order.sku})
# IN VIEWS.PY
def order(request, sku)
...
My guess is that the problem I'm having is caused because since I'm calling an attribute of the OrderTests class, python assumes I wanna send self and then order get the wrong arguments. Easy to solve... just not use it as a class attribute, but I was wondering if there's a way to tell python to not send self in this case.
Thanks.
This happens because in Python functions are descriptors, so when they are accessed on class instances they bind their first (assumed self) parameter to the instance.
You could access _VIEW on the class, not on the instance:
class OrderTests(TestCase, ShopTest):
_VIEW = views.order
def test_gateway_answer(self):
url = 'whatever url'
request = self.request_factory(url, 'GET')
OrderTests._VIEW(request, **{'sku': order.sku})
Alternatively, you can wrap it in staticmethod to prevent it being bound to the instance:
class OrderTests(TestCase, ShopTest):
_VIEW = staticmethod(views.order)
def test_gateway_answer(self):
url = 'whatever url'
request = self.request_factory(url, 'GET')
self._VIEW(request, **{'sku': order.sku})
I want to achieve maximum testability in my Google App Engine app which I'm writing in Python.
Basically what I'm doing is creating an all-purpose base handler which inherits the google.appengine.ext.webapp.RequestHandler. My base handler will expose common functionality in my app such as repository functions, a session object and the like.
When the WSGIApplication receives a request it will find the handler class that has been registered for the requested URL, and call its constructor and after that it will call a method called initialize passing in the request and response objects.
Now, for the sake of testability I want to be able to "mock" these objects (along with my own objects). So my question is how do I go about injecting these mocks? I can override the initialize method in my base handler and check for some global "test flag" and initialize some dummy request and response objects. But it seems wrong (in my mind at least). And how do I go about initializing my other objects (which may depend on the request and response objects)?
As you can probably tell I'm a little new to Python so any recommendations would be most welcome.
EDIT:
It has been pointed out to me that this question was a little hard to answer without some code, so here goes:
from google.appengine.ext import webapp
from ..utils import gmemsess
from .. import errors
_user_id_name = 'userid'
class Handler(webapp.RequestHandler):
'''
classdocs
'''
def __init__(self):
'''
Constructor
'''
self.charset = 'utf8'
self._session = None
def _getsession(self):
if not self._session:
self._session = gmemsess.Session(self)
return self._session
def _get_is_logged_in(self):
return self.session.has_key(_user_id_name)
def _get_user_id(self):
if not self.is_logged_in:
raise errors.UserNotLoggedInError()
return self.session[_user_id_name]
session = property(_getsession)
is_logged_in = property(_get_is_logged_in)
user_id = property(_get_user_id)
As you can see, no dependency injection is going on here at all. The session object is created by calling gmemsess.Session(self). The Session class expects a class which has a request object on it (it uses this to read a cookie value). In this case, self does have such a property since it inherits from webapp.RequestHandler. It also only has the object on it because after calling (the empty) constructor, WSGIApplication calls a method called initialize which sets this object (and the response object). The initialize method is declared on the base class (webapp.RequestHandler).
It looks like this:
def initialize(self, request, response):
"""Initializes this request handler with the given Request and
Response."""
self.request = request
self.response = response
When a request is made, the WSGIApplication class does the following:
def __call__(self, environ, start_response):
"""Called by WSGI when a request comes in."""
request = self.REQUEST_CLASS(environ)
response = self.RESPONSE_CLASS()
WSGIApplication.active_instance = self
handler = None
groups = ()
for regexp, handler_class in self._url_mapping:
match = regexp.match(request.path)
if match:
handler = handler_class()
handler.initialize(request, response)
groups = match.groups()
break
self.current_request_args = groups
if handler:
try:
method = environ['REQUEST_METHOD']
if method == 'GET':
handler.get(*groups)
elif method == 'POST':
handler.post(*groups)
'''SNIP'''
The lines of interest are those that say:
handler = handler_class()
handler.initialize(request, response)
As you can see, it calls the empty constructor on my handler class. And this is a problem for me, because what I think I would like to do is to inject, at runtime, the type of my session object, such that my class would look like this instead (fragment showed):
def __init__(self, session_type):
'''
Constructor
'''
self.charset = 'utf8'
self._session = None
self._session_type = session_type
def _getsession(self):
if not self._session:
self._session = self._session_type(self)
return self._session
However I can't get my head around how I would achieve this, since the WSGIApplication only calls the empty constructor. I guess I could register the session_type in some global variable, but that does not really follow the philosophy of dependency injection (as I understand it), but as stated I'm new to Python, so maybe I'm just thinking about it the wrong way. In any event I would rather pass in a session object instead of it's type, but this looks kind of impossible here.
Any input is appreciated.
The simplest way to achieve what you want would be to create a module-level variable containing the class of the session to create:
# myhandler.py
session_class = gmemsess.Session
class Handler(webapp.Request
def _getsession(self):
if not self._session:
self._session = session_class(self)
return self._session
then, wherever it is that you decide between testing and running:
import myhandler
if testing:
myhandler.session_class = MyTestingSession
This leaves your handler class nearly untouched, leaves the WSGIApplication completely untouched, and gives you the flexibility to do your testing as you want.
Why not just test your handlers in isolation? That is, create your mock Request and Response objects, instantiate the handler you want to test, and call handler.initialize(request, response) with your mocks. There's no need for dependency injection here.