How to mock get_current_user in tornado for unittest? - unit-testing

I have a tornado web application. Many pages require a valid cookie to access. It makes it impossible to test those handlers.
I want to use the mock library to mock the tornado.web.RequestHandler.get_current_user method. But I just cannot get right.
This is how I am doing right now:
class MyUT(tornado.testing.AsyncHTTPTestCase):
def get_app(self):
settings = {
"template_path": '../../../templates',
"cookie_secret": 'secret',
"login_url": '/admin/login',
"debug": True
}
return Application([
(r'/admin/create/super', handlers.CreateSuperUserHandler)
], **settings)
def testGet(self):
with mock.patch.object(handlers.CreateSuperUserHandler, 'get_current_user') as m:
m.return_value = {}
response = self.fetch('/admin/create/super')
print(response.body)
self.assertGreater(response.body.index('create'), 0)
If I execute this code, I got a 404 error. Because I did not define the login hanlder. But I am expecting the mocked get_current_user method return a user object, so do not go to the login page while testing.
One strange thing I found is, if I add m.assert_any_call() in the with block, there's no assertion error.

The #authenticated decorator redirects to the login page if get_current_user returns a falsy value. Try returning a non-empty dict.
You also use handlers.CreateSuperUserHandler in one place and handlers.AdminArea.CreateSuperUserHandler in another; are those the same class object?

Related

How to do unit testing and how it is different from integration testing in frameworks like Django and fastapi?

Let's say I have a Fastapi application like this (This code is taken from documentations):
app = FastAPI()
#app.get("/foo")
async def read_main():
return {"msg": "Hello World"}
I believe there are two ways of testing this view. The first one is by the use of a client object. For instance:
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
However, I think this is not a unit test. It is more like an integration test. Because we are actually running fastapi codes as well.
The second option which in my opinion is closer to unit test definition is to run the function directly:
def test_read_main():
response = read_main()
assert response == {"msg": "Hello World"}
Similarly, in Django, we can directly call the view function, or by using a client object.
My first question is which one is better?
Let's say we chose one of them and now in order to prevent calling the database I have mocked the database. Now I want to test a view that just checks if something exists in the database. My second question is what is the point of testing such view? Becuase after mocking the database, we know what would happen when calling the database with given arguments.

Choosing correct Django delete view approach

I'm working on Django website and I have problem in figuring out correct/good way to handle delete view. From what I found out there are two ways to approach it:
1
class ObjectDeleteView(DeleteView):
model = Object
def get_success_url(self):
objectid = self.kwargs['object_id']
object = Object.objects.get(id = objectid)
container = object.container
containerid = container.id
url = reverse('Containers:showContainerContent', args=[containerid])
return url
def get_object(self):
return get_object_or_404(Object, pk=self.kwargs['object_id'])
2
def objectDelete(request, object_id):
object = Object.objects.get(id = object_id)
container = object.container
containerid = container.id
url = reverse('Containers:showContainerContent', args=[containerid])
return HttpResponseRedirect(url)
From what I can tell both are doing exactly the same thing - once object is deleted present user with page under Containers:showContainerContent.
The big difference I am experiencing is error I am getting when running unit test for this (simple call of the website and check of response code). With option 1 I end up getting error
django.template.exceptions.TemplateDoesNotExist: ContainerApp/object_confirm_delete.html
Which I understand - I don't have this template, this is default template for DeleteView, hence error is correct. The thing is I don't want to have any extra page. Just redirect user and that's it.
Also, I tested changing return url to return HttpResponseRedirect(url) in option 1, but result is the same.
What should I do here? Should I just continue with option 2? What are or might be the drawbacks for this approach?
There is a major difference between two class based delete view and function based view (the way you declared it).
CBV accepts get, post and delete http methods. When you send a get request to class based view, it does not delete the object. Instead it renders template with object to be deleted in context. This is basically used to have confirmation. For example you can send a get request and it will render a template with text "Do you really want to delete?" or "Please confirm blah blah..". And if you send a post or delete request, it will actually delete the object and redirect to next page.
FBV, on the other hand, give you full control over what you want to do. And as you declared it, it will accept any request type and delete the object and redirect to next page because you have not done any request type check in your view which is not a great idea IMHO. You should not allow deletion on get requests. They should be idempotent. There are plenty of otherthings that CBV provides. For example in case the object does not exist your FBV will crash. CBV, on contrary, will return proper 404 response if object does not exist.
So I think there is no bad in using FBV, but make is strong and secure enough that it handles every case (what if object does not exist?, what about confirmation?, GET should be idempotent only allow deletion with post? etc etc). Or simply use CBV.

How to access session variable from response object in django test?

How can i access session variable using response object in django test.
def test(self):
postdata={'user':'staff'}
self.client.login(username='vishnu',password='vishnu#12345')
session=self.client.session
session['user']='manager'
session.save()
response=self.client.post(reverse('userhome'),postdata)
self.assertEqual(session['user'],'staff')
This test fails when run. In the above test, after calling post request the session variable 'user' will change to staff. How do i check the change? session['user'] gives same result after request. However in the view function request.session['user'] gives exact result.
How can i access session data from response?
Checking the final session state in self.client.session is working for me. self.client and response.client are the same object, so you could use response.client.session if that better signals intent:
def test(self):
postdata={'user':'staff'}
self.client.login(username='vishnu',password='vishnu#12345')
session=self.client.session
session['user']='manager'
session.save()
response=self.client.post(reverse('userhome'),postdata)
self.assertIn('user', self.client.session)
self.assertEqual(self.client.session['user'], 'staff')
self.assertIn('user', response.client.session)
self.assertEqual(response.client.session['user'], 'staff')
The documentation would seem to suggest that this wouldn't work: "To modify the session and then save it, it must be stored in a variable first (because a new SessionStore is created every time this property is accessed)." But I guess that's only true of setting the session before sending a request? Not sure, but it could use some clarification.

request issue when unit-testing webapp2 with mock and patch

I'm building unit tests for this webapp2 handler (built for GAE)
class PushNotificationHandler(webapp2.RequestHandler):
def post(self):
UserNotification.parse_from_queue(self.request)
return
app = webapp2.WSGIApplication([
(r'/push/notification', PushNotificationHandler),
], debug=True)
One test is
#patch.object(UserNotification, 'parse_from_queue')
def test_post_webapp(self, p_parse_from_queue):
response = webtest.TestApp(app).post('/push/notification')
eq_(response.status_int, 200)
p_parse_from_queue.assert_called_once_with(response.request)
The HTTP reply is OK, but the mock assertion fails:
Expected call: parse_from_queue(<TestRequest at 0x105a89850 POST http://localhost/push/notification>)
Actual call: parse_from_queue(<Request at 0x105a89950 POST http://localhost/push/notification>)
I can't understand why the request is not the correct one (looks like a deep copy). Is there anything wrong with the unit-test, or is that the way webapp2 handle requests. In the second case, is there a way to test it, without creating a separate test to test PushNotificationHandler.post()
Thanks
I've used mock's call_args in a similar situation. You can do something like this:
request = p_parse_from_queue.call_args[0][0]
self.assertEqual(request.url, "foo")
self.assertEqual(request.*, *)
The [0][0] gives you the first passed argument assuming that you are using ordered arguments and not keyword arguments.
You can then proceed to check other relevant attributes of the request object to make sure it is behaving as expected.

How to check user is authenticated properly in Django and Backbone.js

I want to build single page application using Backbone.js and Django.
For checking user is authenticated or not,
I wrote a method get_identity method in django side.
If request.user.is_authenticated is true it returns request.user.id otherwise it returns Http404
In backbone side, I defined a User model and periodically make ajax call to get_identity.
I think it is the most straightforward way to check user is authenticated or not.
For learning single page application, I want to do this operation more sensible and efficient than this way if it is possible.
So what is your advice about this? When I search Django+Backbone.js + User Authentication, I couldn't find any satisfactory result and I really wonder how people do this simple operation.
Any help or idea will be appreciated.
(By the way I tried to read cookie periodically but HttpOnly True flagged cookies are not reacheable in client side.)
Django views.py
def get_identity(request):
if not request.user.is_authenticated():
raise Http404
return HttpResponse(json.dumps({'identity':request.user.id}), mimetype="application/json")
Backbone.js side.
updateUser:function(){
var $self=this;
$.ajaxSetup({async:false});
$.get(
'/get_identity',
function(response){
// update model...
$self.user.id =response.identity;
//check user every five minutes...
$self.user.fetch({success: function() {
$self.user.set('is_authenticated',true);
setTimeout($self.updateUser, 1000*60*1);
}
},this);
}).fail(function(){
//clear model
$self.user.clear().set($self.user.defaults);
setTimeout($self.updateUser, 1000*60*1);
});
$.ajaxSetup({async:true});
}
I had var is_authenticated = {{request.user.is_authenticated}}; in my base.html
and used the global variable to check.
I'm in pursuit of better solution (because this breaks when you start caching).
But you might find it useful.