Passing self-filled data explicitly in POST request - python

I want to test redirection of Update View.
Reading the documentations, and looking at source code, I find:
Update View instances self.object before processing the request (which helps it to set form fields to current value)
Now this is behavior I wanted, but is giving me trouble during testing.
My Models.py
class Project(models.Model):
title = models.CharField(max_length=50)
description = models.CharField(max_length=200, blank=True, default="")
# Other fields
views.py
class UpdateProject(LogInRequiredMixin, UpdateView):
form_class = ProjectUpdateForm
template_name = 'project/create.html'
And In Testing:
(I have created a project and set up most things in setUp)
def test_redirection(self):
# After updating project, users should land on correct page.[View Project page].
self.client.login(username=self.owner.email, password=self.password)
response = self.client.post(self.url, follow=True) # This will give error, since it requires Title field
self.assertRedirects(response, reverse('project:show',
kwargs={'project_id': self.project.id}))
Now, I know I can pass title explicitly with data field, but if I were to change the fields later in model, this test would also fail, which it shouldn't [It's only purpose is to check for redirection, there are other tests which deal with form validation, etc]
So, my question is: Is there a way to simulate Post Request as done by Update View (i.e. pass data context by setting object context to pre-filled values and only over-ride changed values]

The view presumably only redirects on success. If you don't pass enough information for the view to succeed, then it shouldn't redirect. So it sounds like you want something impossible — you're wanting to test what the code does in a situation other than the situation you are setting up.
The question I have is: what is the value of this test? Why not just put it at the end of the test that checks for success?
I understand that the normal testing mantra is that you have a test for one thing only, but I think it's just impractical for this kind of scenario, and the cost is fragile tests that hack around the internals of something.
The other approach is that you split you write your tests like this:
def _submit_good_form_data(self):
response = self.client.post(self.url, follow=True, data={'title':'The title')
def test_form_success(self):
response = self._submit_good_data()
# asserts here
def test_redirection(self):
response = self._submit_good_data()
# asserts here
This way you only have one place to update when the nature of your form changes.
However, I'd question whether this is really worth it, seeing you are adding significantly to the length of the test run, and it doesn't actually give you the test independence you are really after (an error in the view will cause both tests to fail).

One way to replicate behind-the-scenes machinery:
get_response = self.client.get(self.url)
response = self.client.post(self.url, data=get_response.context['object'].__dict__, follow=True)
self.assertRedirects(response, reverse('project:show', kwargs={'project_id': self.project.id}))
Not very pretty, but is basically what goes behind the scenes.

Related

How to test a view that creates a new team (entity) in a database?

I have to unit test my current project and I've hit a roadblock. I'm at 85% coverage and I need to complete this test to go further. Basically, I need to test that my view is able to create a new team in my database. The Team Model is nested under an Organization in the form of a foreign key.
models.py -
class Organization(models.Model):
orgname = models.CharField(max_length = 100, blank=True)
def __str__(self):
return str(self.orgname)
class Team(models.Model):
teamID = models.AutoField(primary_key=True)
teamName = models.CharField(max_length = 100, blank=True)
org = models.ForeignKey(Organization, on_delete=models.CASCADE)
def __str__(self):
return str(self.teamName)
views_admin.py -
#csrf_exempt
def newTeam(request):
if request.method == 'POST':
param = json.loads(request.body)
orgname = param.get('org')
team = param.get('team')
org = Organization.objects.get(orgname = orgname)
print(org)
Team.objects.create(teamName=team, org=org)
return JsonResponse({'status':200})
urls.py -
path('ajax/newTeam/', views_admin.newTeam, name='ajax_newTeam'),
I have tried this so far:
def test_newTeam(self):
params = {'orgname': REVCYCSCH, 'teamName': 'BLR Scheduling Eng'}
response = self.client.post(path = '/ajax/newTeam/', data = params, content_type = 'application/json')
self.assertEqual(response.status_code, 200)
But I get this error -
As lalithkumar mentions, what you wrote is actually more of an integration test than a proper unit test - a proper unit test would directly call the view function with a handmade request object and mock the model layer.
Now note that this is not a bad thing in itself - integration tests have their use too, and in this case your test reveals a flaw in your view code and your API spec: you're not handling the case where the organisation doesn't exist - actually, you do not handle any error case at all.
IOW, you do want to keep this test - and possibly add other tests submitting other invalid data -, and check that you get a 4XX status code (4xx => bad request) and some error details in your json.
Also, for the nominal case (team successfully created), you may want to return a 201 Created instead (as the response status, not within the json itself !), with a link to the newly created team in the json content.
NB: of course if you don't mock the model layer you have to create the Organization instance in your db before the client.post() call.
EDIT:
Could you tell me how to call the view function with a handmade request object and mock the model layer?
For the first point you want Django's RequestFactory - beware that it can be a bit of a pain to setup. wrt/ mocking, I already linked to the relevant doc. I'm not going to give you a detailed example because it would take much more time than I can afford (and there are some literature on the topic already).
This being said, in your case, I wouldn't bother with it ATM (until your mentor asks for "proper" unit testing, but that's a lot of work since you have to unittest all involved components in isolation, each time with all corner cases - and __still_ write integration tests above).
Your current integration test may not please purists, but it's still effective - it does test the whole chain, and as I said, it already revealed a major issue with your implementation: the complete lack of error handling. In real life programs, it's quite common to have three times more code dealing with errors / corner cases / unexpect conditions than what's actually needed to deal with the nominal case.

Unittest sensitive_post_parameters decorator in django view

I have a view to create new users in my django project.
I am applying the #sensitive_post_parameters decorator to that view to make sure the password isn't logged if there is an unhandled exception or something like that (as indicated in the comments in the source code https://docs.djangoproject.com/en/2.0/_modules/django/views/decorators/debug/).
When I proceed to test the view, I would like to make sure that this protection of the sensitive information is still in place (that I didn't delete the decorator to the function by mistake or something).
I am aware, since the decorator is applied to my function, I can't test it directly from the view tests.
But, for example, with the #login_required decorator, I can test its effects with assertRedirects (as explained here How to test if a view is decorated with "login_required" (Django)).
I have been searching for a way to do that, but I can't find one that works.
I thought of something like this:
def test_senstive_post_parameters(self):
request = RequestFactory().post('create_user', data={})
my_sensitive_parameters = ['password']
self.assertEqual(
request.sensitive_post_parameters,
my_senstive_parameters
)
but that gives me an
AttributeError: 'WSGIRequest' object has no attribute 'sensitive_post_parameters'
Any help would be appreciated.
Even it is telling me I shouldn't be attempting to test this, though I would really like to, as it is seems like an important behaviour that I should make sure remains in my code as it is later modified.
You have created a request using RequestFactory, but you have not actually used it. To test the effect of your view you need to import the view and call it.
from myapp.views import create_user
def test_senstive_post_parameters(self):
request = RequestFactory().post('create_user', data={})
response = create_user(request)
my_sensitive_parameters = ['password']
self.assertEqual(
request.sensitive_post_parameters,
my_senstive_parameters
)

Django 'likes' - ManyToManyField vs new model

I'm implementing likes on profiles for my website and I'm not sure which would be the best practice, a ManyToManyField like so:
class MyUser(AbstractBaseUser):
...
likes = models.ManyToManyField('self', symmetrical = False, null = True)
...
or just creating a class Like, like so:
class Like(models.Model):
liker = models.ForeignKey(MyUser, related_name='liker')
liked = models.ForeignKey(MyUser, related_name='liked')
Is one of them a better choice than the other? If so, why?
thanks
The first option should be preffered. If you need some additional fields to describe the likes, you can still use through="Likes" in your ManyToManyField and define the model Likes.
Manipulating the data entries would be also somewhat more pythonic:
# returns an object collection
likes_for_me = MyUser.objects.filter(pk=1).likes
instead of:
me = MyUser.objects.filter(pk=1)
likes_for_me = Like.objects.filter(liked=me)
The second option is basically what is done internally: a new table is created, which is used to create the links between the entities.
For the first option, you let django do the job for you.
The choice is certainly more about how you want to do the requests. On the second options, you would have to query the Like models that match you model, while on the first one, you only have to request the MyUser, from which you can access the connections.
Second option is more flexible and extensible. For example, you'll probably want to track when like was created (just add Like.date_created field). Also you'll probably want to send notification to content author when content was liked. But at first like only (add Like.cancelled boolead field and wrap it with some logic...).
So I'll go with separate model.
I think the one you choose totally depends on the one you find easier to implement or better. I tend to always use the first approach, as it is more straightforward and logical, at least to me. I also disagree with Igor on that it's not flexible and extensible, you can also initiate notifications when it happens. If you are going to use the Django rest framework, then I totally suggest using the first method, as the second could be a pain.
class Post(models.Model):
like = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='post_like')
Then in your view, you just do this.
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def like(request, id):
signed_in = request.user
post = Post.objects.get(id=id)
if signed_in and post:
post.like.add(signed_in)
# For unlike, remove instead of add
return Response("Successful")
else:
return Response("Unsuccessful", status.HTTP_404_NOT_FOUND)
Then you can use the response however you like on the front end.

Writing good tests for Django applications

I've never written any tests in my life, but I'd like to start writing tests for my Django projects. I've read some articles about tests and decided to try to write some tests for an extremely simple Django app or a start.
The app has two views (a list view, and a detail view) and a model with four fields:
class News(models.Model):
title = models.CharField(max_length=250)
content = models.TextField()
pub_date = models.DateTimeField(default=datetime.datetime.now)
slug = models.SlugField(unique=True)
I would like to show you my tests.py file and ask:
Does it make sense?
Am I even testing for the right things?
Are there best practices I'm not following, and you could point me to?
my tests.py (it contains 11 tests):
# -*- coding: utf-8 -*-
from django.test import TestCase
from django.test.client import Client
from django.core.urlresolvers import reverse
import datetime
from someproject.myapp.models import News
class viewTest(TestCase):
def setUp(self):
self.test_title = u'Test title: bąrekść'
self.test_content = u'This is a content 156'
self.test_slug = u'test-title-bareksc'
self.test_pub_date = datetime.datetime.today()
self.test_item = News.objects.create(
title=self.test_title,
content=self.test_content,
slug=self.test_slug,
pub_date=self.test_pub_date,
)
client = Client()
self.response_detail = client.get(self.test_item.get_absolute_url())
self.response_index = client.get(reverse('the-list-view'))
def test_detail_status_code(self):
"""
HTTP status code for the detail view
"""
self.failUnlessEqual(self.response_detail.status_code, 200)
def test_list_status_code(self):
"""
HTTP status code for the list view
"""
self.failUnlessEqual(self.response_index.status_code, 200)
def test_list_numer_of_items(self):
self.failUnlessEqual(len(self.response_index.context['object_list']), 1)
def test_detail_title(self):
self.failUnlessEqual(self.response_detail.context['object'].title, self.test_title)
def test_list_title(self):
self.failUnlessEqual(self.response_index.context['object_list'][0].title, self.test_title)
def test_detail_content(self):
self.failUnlessEqual(self.response_detail.context['object'].content, self.test_content)
def test_list_content(self):
self.failUnlessEqual(self.response_index.context['object_list'][0].content, self.test_content)
def test_detail_slug(self):
self.failUnlessEqual(self.response_detail.context['object'].slug, self.test_slug)
def test_list_slug(self):
self.failUnlessEqual(self.response_index.context['object_list'][0].slug, self.test_slug)
def test_detail_template(self):
self.assertContains(self.response_detail, self.test_title)
self.assertContains(self.response_detail, self.test_content)
def test_list_template(self):
self.assertContains(self.response_index, self.test_title)
I am not perfect in testing but a few thoughts:
Basically you should test every function, method, class, whatever, that you have written by yourself.
This implies that you don't have to test functions, classes, etc. which the framework provides.
That said, a quick check of your test functions:
test_detail_status_code and test_list_status_code:
Ok to check whether you have configured the routing properly or not. Even more important when you provide your own implementation of get_absolute_url().
test_list_numer_of_items:
Ok if a certain number of items should be returned by the view. Not necessary if the number is not important (i.e. arbitrary).
test_detail_template and test_list_template:
Ok to check whether template variables are correctly set.
All the other functions: Not necessary.
What your are basically testing here is whether the ORM worked properly, whether lists work as expected and whether object properties can be accessed (or not). As long as you don't change e.g. the save() method of a model and/or provide your custom logic, I would not test this. You should trust the framework developers that this works properly.
You only should have to test what you have (over)written.
The model classes are maybe a special case. You basically have to test them, as I said, if you provide custom logic. But you should also test them against your requirements. E.g. it could be that a field is not allowed to be null (or that it has to be a certain datatype, like integer). So you should test that storing an object fails, if it has a null value in this field.
This does not test the ORM for correctly following your specification but test that the specification still fulfills your requirements. It might be that you change the model and change some settings (by chance or because you forgot about the requirements).
But you don't have to test e.g. methods like save() or wether you can access a property.
Of course when you use buggy third party code... well things can be different. But as Django uses the test framework itself to verify that everything is working, I would assume it is working.
To sum up:
Test against your requirements, test your own code.
This is only my point of view. Maybe others have better proposals.
Break your tests into two completely separate kinds.
Model tests. Put these in your models.py file with your model. These tests will exercise the methods in your model classes. You can do simple CRUD (Create, Retrieve, Update, Delete) to simply prove that your model works. Don't test every attribute. Do test field defaults and save() rules if you're curious.
For your example, create a TestNews class that creates, gets, updates and deletes a News item. Be sure to test the default date results. This class should be short and to the point. You can, if your application requires it, test various kinds of filter processing. Your unit test code can (and should) provide examples of the "right" way to filter News.
UI Tests. Put these in a separate tests.py file. These tests will test the view functions and templates.
Name the TestCase with the "condition" you're creating. "TestNotLoggedIn". "TestLoggedIn". "TestNoValidThis". "TestNotAllowedToDoThat". Your setUp will do the login and any other steps required to establish the required condition.
Name each test method with the action and result. "test_get_noquery_should_list", "test_post_should_validate_with_errors", "test_get_query_should_detail".

How do I submit a form given only the HTML source?

I would like to be able to submit a form in an HTML source (string). In other words I need at least the ability to generate POST parameters from a string containing HTML source of the form. This is needed in unit tests for a Django project. I would like a solution that possibly;
Uses only standard Python library and Django.
Allows parameter generation from a specific form if there is more than one form present.
Allows me to change the values before submission.
A solution that returns a (Django) form instance from a given form class is best. Because it would allow me to use validation. Ideally it would consume the source (which is a string), a form class, and optionally a form name and return the instance as it was before rendering.
NOTE: I am aware this is not an easy task, and probably the gains would hardly justify the effort needed. But I am just curious about how this can be done, in a practical and reliable way. If possible.
You should re-read the documentation about Django's testing framework, specifically the part about testing views (and forms) with the test client.
The test client acts as a simple web browser, and lets you make GET and POST requests to your Django views. You can read the response HTML or get the same Context object the template received. Your Context object should contain the actual forms.Form instance you're looking for.
As an example, if your view at the URL /form/ passes the context {'myform': forms.Form()} to the template, you could get to it this way:
from django.test.client import Client
c = Client()
# request the web page:
response = c.get('/form/')
# get the Form object:
form = response.context['myform']
form_data = form.cleaned_data
my_form_data = {} # put your filled-out data in here...
form_data.update(my_form_data)
# submit the form back to the web page:
new_form = forms.Form(form_data)
if new_form.is_valid():
c.post('/form/', new_form.cleaned_data)
Hopefully that accomplishes what you want, without having to mess with parsing HTML.
Edit: After I re-read the Django docs about Forms, it turns out that forms are immutable. That's okay, though, just create a new Form instance and submit that; I've changed my code example to match this.
Since the Django test framework does this, I'm not sure what you're asking.
Do you want to test a Django app that has a form?
In which case, you need to do an initial GET
followed by the resulting POST
Do you want to write (and test) a Django app that submits a form to another site?
Here's how we test Django apps with forms.
class Test_HTML_Change_User( django.test.TestCase ):
fixtures = [ 'auth.json', 'someApp.json' ]
def test_chg_user_1( self ):
self.client.login( username='this', password='this' )
response= self.client.get( "/support/html/user/2/change/" )
self.assertEquals( 200, response.status_code )
self.assertTemplateUsed( response, "someApp/user.html")
def test_chg_user( self ):
self.client.login( username='this', password='this' )
# The truly fussy would redo the test_chg_user_1 test here
response= self.client.post(
"/support/html/user/2/change/",
{'web_services': 'P',
'username':'olduser',
'first_name':'asdf',
'last_name':'asdf',
'email':'asdf#asdf.com',
'password1':'passw0rd',
'password2':'passw0rd',} )
self.assertRedirects(response, "/support/html/user/2/" )
response= self.client.get( "/support/html/user/2/" )
self.assertContains( response, "<h2>Users: Details for", status_code=200 )
self.assertContains( response, "olduser" )
self.assertTemplateUsed( response, "someApp/user_detail.html")
Note - we don't parse the HTML in detail. If it has the right template and has the right response string, it has to be right.
It is simple... and hard at the same time.
Disclaimer: I don't know much about Python and nothing at all about Django... So I give general, language agnostic advices...
If one of the above advices doesn't work for you, you might want to do it manually:
Load the page with an HTML parser, list the forms.
If the method attribute is POST (case insensitive), get the action attribute to get the URL of the request (can be relative).
In the form, get all input and select tags. The name (or id if no name) attributes are the keys of the request parameters. The value attributes (empty if absent) are the corresponding values.
For select, the value is the one of the selected option or the displayed text is no value attribute.
These names and values must be URL encoded in GET requests, but not in POST ones.
HTH.
Check out mechanize or it's wrapper twill. I think it's ClientForm module will work for you.

Categories