Django Test Client does not create database entries - python

I'm creating unit tests for my views using Django's built-in Test Client to create mock requests.
The view I'm calling should create an object in the database. However, when I query the database from within the test method the object isn't there - it either hasn't been created or has been discarded on returning from the view.
Here's the view:
def apply_to_cmp(request, campaign_id):
""" Creates a new Application to 'campaign_id' for request.user """
campaign = Campaign.objects.get(pk = campaign_id)
if not Application.objects\
.filter(campaign = campaign, user = request.user)\
.exists():
application = Application(**{'campaign' : campaign,
'user' : request.user})
application.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
This is the test that calls it:
def test_create_campaign_app(self):
""" Calls the method apply_to_cmp in .views """
c = Client()
c.login(username = self.username, password = self.password)
url = '/campaign/' + self.campaign.id + '/apply/'
response = c.get(url)
# Check whether request was successful (should return 302: redirect)
self.assertEqual(response.status_code, 302)
# Verify that an Application object was created
app_count = Application.objects\
.filter(user = self.user, campaign = self.campaign)\
.count()
self.assertEqual(app_count, 1)
This is the output from the running the test:
Traceback (most recent call last):
File "/test_views.py", line 40, in test_create_campaign_app
self.assertEqual(app_count, 1)
AssertionError: 0 != 1
The method apply_to_cmp is definitely being called, since response.status_code == 302, but still the Application object is not created. What am I doing wrong?
Edit: Solution
Client.login failed because the login system was not properly initialised in the setUp method. I fixed this by calling call_command('loaddata', 'initial_data.json') with initial_data.json containing the setup for the login system. Also, HttpResponseRedirect(request.META.get('HTTP_REFERER')) didn't work for obvious reasons. I changed that bit to
if request.META.get('HTTP_REFERER'):
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
return HttpResponse()
And therefore the test to
self.assertEqual(response.status_code, 200)
Thanks for your help!

Nothing stands out as particularly wrong with your code - but clearly either your test case or the code your are testing is not working the way you think. It is now time to question your assumptions.
The method apply_to_cmp is definitely being called, since response.status_code == 302
This is your first assumption, and it may not be correct. You might get a better picture of what is happening if you examine other details in the response object. For example, check the response.redirect_chain and confirm that it actually redirects where you expect it to:
response = c.get(url, follow=True)
self.assertEqual(response.redirect_chain, [<expected output here>])
What about other details? I can't see where self.username and self.password are defined from the code you provided. Are you 100% sure that your test code to login worked? c.login() returns 'True' or 'False' to indicate if the login was successful. In my test cases, I like to confirm that the login succeeds.
login_success = c.login(username = self.username, password = self.password)
self.assertTrue(login_success)
You can also be a bit more general. You find nothing if you check Application.objects.filter(user=self.user, campaign=self.campaign), but what about checking Application.objects.all()? You know that a specific item isn't in your database, but do you know what is stored in the database (if anything at all) in the test code at that time? Do you expect other items in there? Check to confirm that what you expect is true.
I think you can solve this one, but you'll need to be a bit more aggressive in your analysis of your test case, rather than just seeing that your app_count variable doesn't equal 1. Examine your response object, put in some debug statements, and question every assumption.

First of all, if you are subclassing from django.test.TestCase, please take in consideration the fact that each test is wrapped into transactions (official docs).
Then, you can add db logging to your project to see whether there was a hit to the database or not (official docs).
And finally be sure that you're using correct lookups at this line: filter(user = self.user, campaign = self.campaign)

Related

Adding methods gives a 'index out of range error'?

When adding a vital component of methods=["POST", "GET"], my code gives the error:
Line 127, in PatientDashboard
""".format(Data[0][0]))
IndexError: list index out of range
I understand what this error normally means but I don't understand how adding methods affect the size of my list.
#app.route("/PatientDashboard.html", methods=["GET", "POST"])
def PatientDashboard():
Username = (request.args.get("Username"))
Connection = sqlite3.connect(DB)
Cursor = Connection.cursor()
Data = Cursor.execute("""
SELECT *
FROM PatientTable
WHERE Username = '{}'
""".format(Username))
Data = Data.fetchall()
AllAppointments = Cursor.execute("""
SELECT Title, Firstname, Surname, TimeSlot, Date, Status
FROM AppointmentTable
INNER JOIN DoctorTable ON AppointmentTable.DoctorID = DoctorTable.DoctorID
WHERE PatientID = '{}'
""".format(Data[0][0]))
AllAppointments = AllAppointments.fetchall()
The SQL statements work perfectly (database isn't empty) and when adding print(Data) after the first SQL statement there is an output of a nested list.
I have tried troubleshooting by looking at various other questions on stackoverflow but with no luck.
Thank you ever so much in advance.
EDIT 1:
Username = (request.args.get("Username"))
print("Username: ", Username)
Gives the correct output, e.g. Username: nx_prv but after using the POST request the output becomes Username: None.
EDIT 2:
I have managed to fix this using flask.sessions. The problem was that the request.args.get("Username") was getting 'reset' every time.
The scenario I envision: the route was tested with a GET method (because there was not methods argument), and everything was fine. The methods argument was added so a POST could be tested, and it "stopped working". But it really didn't stop working, it's just not built to handle a POST request.
From flask doc on request object the two salient attributes are:
form
A MultiDict with the parsed form data from POST or PUT requests. Please keep in mind that file uploads will not end up here, but
instead in the files attribute.
args
A MultiDict with the parsed contents of the query string. (The part in the URL after the question mark).
So a GET request will "populate" args and a POST request, form. Username will be None from this line Username = (request.args.get("Username")) on a POST request.
You can determine which method by interrogating the method attribute of the request object.
method
The current request method (POST, GET etc.)

Session data not being stored during testing in Django

I am currently writing tests for our project, and I ran into an issue. We have this section of a view, which will redirect the user back to the page where they came from including an error message (that's being stored in the session):
if request.GET.get('error_code'):
"""
Something went wrong or the call was cancelled
"""
errorCode = request.GET.get('error_code')
if errorCode == 4201:
request.session['errormessage'] = _('Action cancelled by the user')
return HttpResponseRedirect('/socialMedia/manageAccessToken')
Once the HttpResponseRedirect kicks in, the first thing that the new view does is scan the session, to see if any error messages are stored in the session. If there are, we place them in a dictionary and then delete it from the session:
def manageAccessToken(request):
"""
View that handles all things related to the access tokens for Facebook,
Twitter and Linkedin.
"""
contextDict = {}
try:
contextDict['errormessage'] = request.session['errormessage']
contextDict['successmessage'] = request.session['successmessage']
del request.session['errormessage']
del request.session['successmessage']
except KeyError:
pass
We should now have the error message in a dictionary, but after printing the dictionary the error message is not there. I also printed the session just before the HttpResponseRedirect, but the session is an empty dictionary there as well.
This is the test:
class oauthCallbacks(TestCase):
"""
Class to test the different oauth callbacks
"""
def setUp(self):
self.user = User.objects.create(
email='test#django.com'
)
self.c = Client()
def test_oauthCallbackFacebookErrorCode(self):
"""
Tests the Facebook oauth callback view
This call contains an error code, so we will be redirected to the
manage accesstoken page. We check if we get the error message
"""
self.c.force_login(self.user)
response = self.c.get('/socialMedia/oauthCallbackFacebook/',
data={'error_code': 4201},
follow=True,
)
self.assertEqual('Action cancelled by the user', response.context['errormessage'])
It looks like the session can not be accessed or written to directly from the views during testing. I can, however, access a value in the session by manually setting it in the test by using the following bit of code:
session = self.c.session
session['errormessage'] = 'This is an error message'
session.save()
This is however not what I want, because I need the session to be set by the view as there are many different error messages in the entire view. Does anyone know how to solve this? Thanks in advance!
After taking a closer look I found the issue, it is in the view itself:
errorCode = request.GET.get('error_code')
if errorCode == 4201:
request.session['errormessage'] = _('Action cancelled by the user')
The errorCode variable is a string, and I was comparing it to an integer. I fixed it by changing the second line to:
if int(errorCode) == 4201:

Picture property format using run with friends fb app

I am editing the Runwithfriends FB sample app to build one of my own. It was working fine and I was making a few changes here and there. I took a break from it for about a fortnight but now when I try to access the app, I get a strange python error:
C:\Program Files\Apache Software Foundation\Tomcat 7.0\webapps\ROOT\app\main.py in init_facebook(self=<main.RecentRunsHandler object>)
316 user_id=facebook.user_id, friends=friends,
317 access_token=facebook.access_token, name=me[u'name'],
=> 318 email=me.get(u'email'), picture=me[u'picture'])
319 user.put()
320 except KeyError, ex:
<..some portion clipped..>
class 'google.appengine.api.datastore_errors.BadValueError'>: Property picture must be a str or unicode instance, not a dict
args = ('Property picture must be a str or unicode instance, not a dict',)
message = 'Property picture must be a str or unicode instance, not a dict'"
I know this is a very generic error but its pointing to the following code. This code has always been there and I have never touched it. I really do not know where else to look now - I have searched exhaustively but couldnt find a clue. Sorry, if this is still too broad but I would be glad if you can tell me what other info can I provide to debug this :-(
def init_facebook(self):
"""Sets up the request specific Facebook and User instance"""
facebook = Facebook()
user = None
# initial facebook request comes in as a POST with a signed_request
if u'signed_request' in self.request.POST:
facebook.load_signed_request(self.request.get('signed_request'))
# we reset the method to GET because a request from facebook with a
# signed_request uses POST for security reasons, despite it
# actually being a GET. in webapp causes loss of request.POST data.
self.request.method = u'GET'
self.set_cookie(
'u', facebook.user_cookie, datetime.timedelta(minutes=1440))
elif 'u' in self.request.cookies:
facebook.load_signed_request(self.request.cookies.get('u'))
# try to load or create a user object
if facebook.user_id:
user = User.get_by_key_name(facebook.user_id)
if user:
# update stored access_token
if facebook.access_token and \
facebook.access_token != user.access_token:
user.access_token = facebook.access_token
user.put()
# refresh data if we failed in doing so after a realtime ping
if user.dirty:
user.refresh_data()
# restore stored access_token if necessary
if not facebook.access_token:
facebook.access_token = user.access_token
if not user and facebook.access_token:
me = facebook.api(u'/me', {u'fields': _USER_FIELDS})
try:
friends = [user[u'id'] for user in me[u'friends'][u'data']]
user = User(key_name=facebook.user_id,
user_id=facebook.user_id, friends=friends,
access_token=facebook.access_token, name=me[u'name'],
email=me.get(u'email'), picture=me[u'picture'])
user.put()
except KeyError, ex:
pass # ignore if can't get the minimum fields
self.facebook = facebook
self.user = user
Might have to do with the October 2012 Breaking Changes, quote:
/picture connection will return a dictionary when a callback is specified
We will start returning a dictionary containing the fields url, height, width, and is_silhouette when accessing the /picture connection for an object and specifying a callback property. Currently we just return the picture URL as a string.
So at this point in your code, where you are currently using picture=me[u'picture'], try accessing the url property of the picture dictionary instead. (If it has one; I can’t tell you for sure if this is applicable, since I don’t know if your code would be considered as having specified a callback property.)
If my assumption is correct, you could also enable the migration as described in the roadmap; but that will only make your app work in the old way until Oct. 3rd, so probably better to try and fix it right away.
This is the way to get the picture:
picture=me[u'picture'][u'data'][u'url']

Why am I getting a broken pipe error when adding data to the database?

Not sure if there's a better way to do this but I have a sign up page on my site and after a user signs up I add their initial data(stuff in the __init__ data model) then I start adding some other info in the same section which is giving me a broken pipe error. Oddly the code seems to work since the entries I'm expecting are in the database. I have tried moving around the .flush() command to see if it helps but it doesn't seem to be.
Traceback (most recent call last):
File "/Users/me/Dropbox/code/eclipseWorkSpace/website/pyramidwiki/lib/python2.7/site-packages/waitress-0.8.1-py2.7.egg/waitress/channel.py", line 134, in handle_write
flush()
File "/Users/me/Dropbox/code/eclipseWorkSpace/website/pyramidwiki/lib/python2.7/site-packages/waitress-0.8.1-py2.7.egg/waitress/channel.py", line 249, in _flush_some
num_sent = self.send(chunk)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/asyncore.py", line 365, in send
result = self.socket.send(data)
error: [Errno 32] Broken pipe
Here's my code:
if 'form.submitted' in request.params:
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
password = request.params['password']
try:
new_user = Users(email, firstname, lastname, password)
DBSession.add(new_user)
#DBSession.flush() #commit so we get error if any
#add some other info
user_data = DBSession.query(Users).filter(Users.email==email).first()
user_data.join_date = datetime.datetime.now()
#create random number for verification url
user_data.vertified = id_generator(50)
DBSession.flush() #doesn't seem to make a difference where the flush is
return HTTPFound(location = request.route_url('new'))
Any ideas?
This may not answer you question directly, but "you're doing it all wrong"(tm) :)
You don't need to re-query User object after you added it to the session - what's more, trying to query it from the database without doing session.flush() first will result in an error because there's no record in the database yet. I'd do something like this:
if 'form.submitted' in request.params:
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
password = request.params['password']
try:
new_user = Users(email, firstname, lastname, password)
new_user.join_date = datetime.datetime.now()
new_user.verified = id_generator(50)
DBSession.add(new_user)
DBSession.flush() # should fail if user email is in the database
return HTTPFound(location = request.route_url('new'))
Also, you need to check that all branches of execution (i.e. the except: clause, the else: clause of "if 'form.submitted' in request.params" return something meaningful - you may be getting the exception because your view function returns None in some conditions. Actually, that's probably what was happening - the "user_data = DBSession.query(Users)" line was raising an exception, and the except: part was not returning anything
I too struggled with the same problem on my Pyramid project and contrary to comments on github, it wasn't with waitress.
In my case, the problem of Error: Broken Pipe only occured whenever I used redirects (HTTPFound, HTTPMovedPermanently etc.). Since your function also uses HTTPFound, I think the problem is the same.
Atleast for me, this error was due to pyramid_debugtoolbar extension. The reason is probably that whenever our view does soemthing like
return HTTPFound(foo)
it sends out a 302 in the header and a Connection:Close. The pyramid_debugtoolbar extension adds a lengthy body to the response. The client on seeing the header, closes the connection and does not accept the lengthy debug body. Hence the Broken Pipe message (Atleast that is what Wireshark shows).
Try disabling the pyramid_debugtoolbar in your app's .ini, it might help.

Python Decorator for GAE Web-Service Security Check

In this post, Nick suggested a decoartor:
Python/WebApp Google App Engine - testing for user/pass in the headers
I'm writing an API to expose potentially dozens of methods as web-services, so the decorator sounds like a great idea.
I tried to start coding one based on this sample:
http://groups.google.com/group/google-appengine/browse_thread/thread/ac51cc32196d62f8/aa6ccd47f217cb9a?lnk=gst&q=timeout#aa6ccd47f217cb9a
I need it compatible with Python 2.5 to run under Google App Engine (GAE).
Here's my attempt. Please just point the way to if I'm on the right track or not.
Currently getting an error "Invalid Syntax" on this line:
class WSTest(webapp.RequestHandler):
My idea is to pass an array of roles to the decorator. These are the only roles (from my db that should have access to each various web service).
def BasicAuthentication(roles=[]):
def _decorator(func):
def _wrapper(*args, **kwds):
logging.info("\n\n BasicAuthentication:START:__call__ \n\n")
auth = None
if 'Authorization' in self.request.headers:
auth = self.request.headers['Authorization']
if not auth:
self.response.headers['WWW-Authenticate'] = 'Basic realm="MYREALM"'
self.response.set_status(401)
self.response.out.write("Authorization required")
logging.info ("\n\n Authorization required \n\n")
return
(username, password) = base64.b64decode(auth.split(' ')[1]).split(':')
logging.info ("\n\n username = " + username + " password=" + password + "\n\n")
isValidUserPass = False
usersSimulatedRole = "Admin"
#check against database here...
if user == "test12" and password == "test34":
isValidUserPass = True
isValidRole = False
if usersSimulatedRole in roles:
isValidRole = True
#next check that user has one of the roles
# TODO
if not isValidUserPass:
self.response.set_status(403)
self.response.out.write("Forbidden: Userid/password combination failed")
logging.info("\n\n BasicAuthentication:END:__call__ \n\n")
return func(*args, **kwds)
return _wrapper
return _decorator
#BasicAuthentication(["Admin","Worker"]) #list of roles that can run this function
class WSTest(webapp.RequestHandler):
def get(self):
logging.info("\n\n\n WSTest \n\n")
...etc...
Thanks,
Neal Walters
You need to write a method decorator, not a class decorator: As lost-theory points out, class decorators don't exist in Python 2.5, and they wouldn't work very well in any case, because the RequestHandler class isn't initialized with request data until after it's constructed. A method decorator also gives you more control - eg, you could allow GET requests unauthenticated, but still require authentication for POST requests.
Other than that, your decorator looks fine - just apply it to the relevant methods. The only change I would really suggest is replacing the .set_status() calls with .error() calls and remove the response.write calls; this allows you to override .error() on the RequestHandler class to output a nice error page for each possible status code.
Class decorators were added in Python 2.6.
You'll have to manually wrap the class or think of another solution to work under 2.5. How about writing a decorator for the get method instead?

Categories

Resources