I'm new to unit testing and I've been trying to test a GET method of the card game that I've built.
My TestCase is the following:
def test_rooms(self):
c = APIClient()
room_id = PokerRoom.objects.get(name='planning').id
request = c.get('http://127.0.0.1:8000/room/{}'.format(room_id))
print(request)
The id is a UUID that's why I'm using the room_id method.
My url:
path('room/<int:pk>', room),
where room is a #api_view(['GET']) method and pk is the id of the room. But when I try to test it, an error occurs:
<HttpResponseNotFound status_code=404, "text/html">
Checked if the room exists in the test database and it exists, now I don't know what is happening. Can someone help me?
Can you add more details to your code above, such as how the test suite has been created, how is the data set up, etc? One problem I am noticing straight away is how the request is being made. Why are you using a complete URL? If you are using the Django/DRF test API client, you should use the view path URL instead of the complete URL.
Reference: https://docs.djangoproject.com/en/4.0/topics/testing/tools/#overview-and-a-quick-example
Related
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.
Currently I am investigating using graphene to build my Web server API. I have been using Django-Rest-Framework for quite a while and want to try something different.
I have figured out how to wire it up with my existing project and I can test the query from Graphiql UI, by typing something like
{
industry(id:10) {
name
description
}
}
Now, I want to have the new API covered by Unit/integration tests. And here the problem starts.
All the documentation/post I am checking on testing query/execution on graphene is doing something like
result = schema.execute("{industry(id:10){name, description}}")
assertEqual(result, {"data": {"industry": {"name": "Technology", "description": "blab"}}}
My point is that the query inside execute() is just a big chunk of text and I don't know how I can maintain it in the future. I or other developer in the future has to read that text, figure out what it means and update it if needed.
Is that how this supposed to be? How do you guys write unit test for graphene?
I've been writing tests that do have a big block of text for the query, but I've made it easy to paste in that big block of text from GraphiQL. And I've been using RequestFactory to allow me to send a user along with the query.
from django.test import RequestFactory, TestCase
from graphene.test import Client
def execute_test_client_api_query(api_query, user=None, variable_values=None, **kwargs):
"""
Returns the results of executing a graphQL query using the graphene test client. This is a helper method for our tests
"""
request_factory = RequestFactory()
context_value = request_factory.get('/api/') # or use reverse() on your API endpoint
context_value.user = user
client = Client(schema) # Note: you need to import your schema
executed = client.execute(api_query, context_value=context_value, variable_values=variable_values, **kwargs)
return executed
class APITest(TestCase):
def test_accounts_queries(self):
# This is the test method.
# Let's assume that there's a user object "my_test_user" that was already setup
query = '''
{
user {
id
firstName
}
}
'''
executed = execute_test_client_api_query(query, my_test_user)
data = executed.get('data')
self.assertEqual(data['user']['firstName'], my_test_user.first_name)
...more tests etc. etc.
Everything between the set of ''' s ( { user { id firstName } } ) is just pasted in from GraphiQL, which makes it easier to update as needed. If I make a change that causes a test to fail, I can paste the query from my code into GraphQL, and will often fix the query and paste a new query back into my code. There is purposefully no tabbing on this pasted-in query, to facilitate this repeated pasting.
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.
I am writing unit tests to validate a profile avatar module. So, I have a form that allows a user to upload an avatar. If one exists, it simply replaces the current one.
In my test, I do the following (the class setup logs a user in - not shown here):
f = open('testfile1.jpg')
data = {'image':f}
response = self.client.post('/profile/uploadavatar/',data)
self.assertEqual(response.status_code, 200)
self.assertEqual(self.user1.get_profile().avatar.image.name, u'uploads/images/testfile1.jpg')
f.close()
f = open('testfile2.jpg')
data = {'image':f}
response = self.client.post('/profile/uploadavatar/',data)
self.assertEqual(response.status_code, 200)
self.assertEqual(self.user1.get_profile().avatar.image.name, u'uploads/images/testfile2.jpg')
f.close()
The second assertEqual to test for avatar image name always fails because it is still set to the first filename (testfile1.jpg). However when I test this manually the code does what I think it should, which is replace the old avatar with the new one.
Am I doing something wrong? I'm new to the django unit tests so I may be missing something very simple...
Any ideas would be appreciated.
Thanks in advance!
The "self.user1" object, along with the profile, are cached at the beginning.
Reload the user/profile objects between actions to see updated data.
(Pulled up from the comments.)
I'm trying to call a view directly from another (if this is at all possible). I have a view:
def product_add(request, order_id=None):
# Works. Handles a normal POST check and form submission and redirects
# to another page if the form is properly validated.
Then I have a 2nd view, that queries the DB for the product data and should call the first one.
def product_copy_from_history(request, order_id=None, product_id=None):
product = Product.objects.get(owner=request.user, pk=product_id)
# I need to somehow setup a form with the product data so that the first
# view thinks it gets a post request.
2nd_response = product_add(request, order_id)
return 2nd_response
Since the second one needs to add the product as the first view does it I was wondering if I could just call the first view from the second one.
What I'm aiming for is just passing through the request object to the second view and return the obtained response object in turn back to the client.
Any help greatly appreciated, critism as well if this is a bad way to do it. But then some pointers .. to avoid DRY-ing.
Thanx!
Gerard.
My god, what was I thinking. This would be the cleanest solution ofcourse:
def product_add_from_history(request, order_id=None, product_id=None):
""" Add existing product to current order
"""
order = get_object_or_404(Order, pk=order_id, owner=request.user)
product = Product.objects.get(owner=request.user, pk=product_id)
newproduct = Product(
owner=request.user,
order = order,
name = product.name,
amount = product.amount,
unit_price = product.unit_price,
)
newproduct.save()
return HttpResponseRedirect(reverse('order-detail', args=[order_id]) )
A view is a regular python method, you can of course call one from another giving you pass proper arguments and handle the result correctly (like 404...). Now if it is a good practice I don't know. I would myself to an utiliy method and call it from both views.
If you are fine with the overhead of calling your API through HTTP you can use urllib to post a request to your product_add request handler.
As far as I know this could add some troubles if you develop with the dev server that comes with django, as it only handles one request at a time and will block indefinitely (see trac, google groups).