I read the official doc for generating the unit tests but it is too messy. I have to generate a unit test cases for the AdminPasswordChangeForm, and two defined function in views.py which are changing the status of is_staff and is_active.
I have also wrote simple test cases to register an user. Follow is the method defined in views.py (which are updating the password using AdminPasswordChangeForm)
def user_change_password(request, id):
user = User.objects.get(pk=id)
form = AdminPasswordChangeForm(user, request.POST)
if form.is_valid():
new_user = form.save()
msg = _('Password changed successfully.')
request.user.message_set.create(message=msg)
return HttpResponseRedirect('../../user/users')
else:
form = AdminPasswordChangeForm(user)
extra_context = {
'form': form,
'change': True
}
return direct_to_template(request,"users/user_password_change.html",
extra_context = extra_context)
There are lots of tutorials on how to write tests. Django Documentation (like always) does a great job of explaining what their framework offers in terms of testing. https://docs.djangoproject.com/en/dev/topics/testing/?from=olddocs
In addition there are many articles/slideshows covering WHAT to test.
http://toastdriven.com/blog/2011/apr/10/guide-to-testing-in-django/
As far as HOW to test a view you have a couple of options
You can use the built in request factory to generate a request object and call your function directly. You can make sure a redirect is returned if appropriate or a template or whatever you are expecting.
Or you can make a request to the url in your urls.py file.
I mean to test AdminPasswordChangeForm instantiate it with a user object and a POST dictionary. Check to make sure it is creating the correct form, If you haven't customized it too much you can skip testing it becuase django does a great job of testing their code.
Related
I'm attempting to overwrite the RegistrationView that is part of django registration redux.
https://github.com/macropin/django-registration/blob/master/registration/backends/default/views.py
Essentially, I want to implement logic that prevents the registration from happening if some condition is present. Checking for this condition requires access to the request.
I'm thinking that I might be able to subclass the register function within this Class based view.
def register(self, form):
#START OF MY CODE
result = test_for_condition(self.request)
if result == False:
messages.error(self.request, "Registration cannot be completed.", extra_tags='errortag')
return redirect('/access/register')
#END OF MY CODE
site = get_current_site(self.request)
if hasattr(form, 'save'):
new_user_instance = form.save(commit=False)
else:
new_user_instance = (UserModel().objects
.create_user(**form.cleaned_data))
new_user = self.registration_profile.objects.create_inactive_user(
new_user=new_user_instance,
site=site,
send_email=self.SEND_ACTIVATION_EMAIL,
request=self.request,
)
signals.user_registered.send(sender=self.__class__,
user=new_user,
request=self.request)
return new_user
What is the correct way to do this using class based views? I've worked almost exclusively with function based views so far.
Thanks!
As per my knowledge, it is recommended to subclass "AsbtractBaseUser" and "BaseUserManager" for user management in Django.
In CustomUserModel, you can define required fields added to the existing fields from BaseUserManager.
In CustomUserManager, you can override create_user, create_staff_user, create_superuser, and save_user. You can define a few conditions that should be checked before registering the user and also you can add permissions or groups to the user based on the role in the "save_user" method.
Create CustomRegistrationSerializer, that controls the fields that need to be displayed on the registration page, and validation of those fields can also be defined.
In POST method of CBV, you can check condition that requires "request" before serializing data. If condition is met, you can continue with serializing and saving otherwise, you can return appropriate response.
Django : https://docs.djangoproject.com/en/3.2/topics/auth/customizing/#extending-the-existing-user-model
In a Django 1.11 app I've created a large form for users to update a model instance, but based on what the users change there may be multiple other forms I'd like to redirect them to afterwards.
What's the best practise for dynamically chaining multiple forms (dynamic workflow)?
I can't find anything helpful in the Django docs or other questions on here.
E.g. (extremely simplified) view for model update form:
def asset_edit(request, pk):
asset = get_object_or_404(Asset, pk=pk)
current_location_concat = "{} {} {}".format(asset.location_building, asset.location_room, asset.location_area)
if request.method == "POST":
form = EditAsset(request.POST, request.FILES, instance=asset)
if form.is_valid():
asset = form.save(commit=False)
# setting some things...
asset.save()
new_location_concat = "{} {} {}".format(asset.location_building, asset.location_room, asset.location_area)
if current_location_concat != new_location_concat:
check_relatives = True # redirect to check_relatives form
if asset.equipment_id:
if Asset.objects.filter(equipment_id=asset.equipment_id).count() > 1:
duplicate_assets = True # redirect to check_duplicates form
# if check_relatives and duplicate_assets:
# redirect to check_duplicates form and then on to check_relatives form
return redirect("asset_detail", pk=asset.pk)
I know I could just add a new URL for my check_duplicates form with a "next" (or similar) parameter, pass a "next" value that the check_duplicate view maps to one or more other forms (just "check_relatives" in this instance) and redirects to this when the check_duplicates form is submitted, but is this the best practise?
Especially given that the number of forms that may need to be chained this way could get quite large and the logic complex!
I have been using formtool's WizardView for this and I have tried the 'stuff-everything-into-the-post-method' approach as well.
WizardView looks like the right fit for what you are trying to do.
For example, it allows you to skip steps in your workflow based on function conditions - removing this bit of logic from your workflow, which makes the whole view easier to read.
I have found it a pain to write tests for such views as you have to account for the WizardView's very own internal structure while testing, but overall it's definitely better than writing a 1000 lines post-method (might as well code in BASIC then).
The decorator is working fine but I would like to display an error message (I'd like to use messages framework) if the user doesn't belong to any of the required groups. Here's the decorator:
def group_required(*group_names):
"""Requires user membership in at least one of the groups passed in."""
def in_groups(user):
if user.is_authenticated():
if bool(user.groups.filter(name__in=group_names)) or user.is_superuser:
return True
return False
return user_passes_test(in_groups)
I call it using something like:
#require_http_methods(['GET'])
#group_required('supervisor')
def home_view(request):
return render(request, 'home.html')
I tried using this snippet to use messages framework (since this requires the request object) but it realized that messages framework middleware didn't appear installed inside the decorator.
I'm willing to change whatever it takes :)
Update:
What I'm looking for:
def group_required(request, *group_names):
"""Requires user membership in at least one of the groups passed in."""
def in_groups(user):
if user.is_authenticated():
if user.groups.filter(name__in=group_names).exists() or user.is_superuser:
return True
else:
# I'm getting:
# You cannot add messages without installing django.contrib.messages.middleware.MessageMiddleware
messages.add_message(request, messages.ERROR, 'Group is not allowed')
return False
return user_passes_test(in_groups, request)
I don't think you really need threadlocals in this use case. And normally when threadlocals seems to be the only way to go in a Django app, there could be some mis-structured context layers. Comparing w/ the threadlocals, I would rather to duplicate user_passes_test and then modify it to pass request to in_groups (We could not pass request to is_group easily without modifying the code of user_passes_test. Check the question: How to pass Django request object in user_passes_test decorator callable function.) (Maybe a ticket for this?)
Furthermore, bool(user.groups.filter(name__in=group_names)) would caused items to be retrieved to DB adapter and Python instance before deciding the existence, using exists() and thus user.groups.filter(name__in=group_names).exists() to directly return bool result from DB backend is far more efficient here.
I'm developing an app with django, and I have a view where I use 2 return render_to_response, with two different html files, depending on the status of the user.
I was wondering if it would be a better practice to split my view into two different views or if I should keep a bigger view.
What are the pros and the cons of doing so?
Sorry if my question is not clear. Thank you very much for your advices.
There's no right or wrong answer to this question so your question may not be acceptable on stackoverflow, which usually is intended for questions/problems with specific technical solutions.
That said, here's my view on this topic - I personally like to keep my view function small and if further processing is required, split them out into smaller functions.
For example:-
#permission_required('organizations.organization_add_user')
def organization_add_user(request, org_slug):
org = get_object_or_404(Organization, slug=org_slug)
form = OrganizationAddUserForm(org=org)
if request.method == 'POST':
form = OrganizationAddUserForm(request.POST or None, request.FILES or None, org=org)
if form.is_valid():
cd = form.cleaned_data
# Create the user object & send out email for activation
user = create_user_from_manual(request, data=cd)
# Add user to OrganizationUser
org_user, created = OrganizationUser.objects.get_or_create(user=user,\
organization=org)
dept = org.departments.get(name=cd['department'])
org_user.departments.add(dept)
# Add user to the appropriate roles (OrganizationGroup) and groups (django groups)
org_groups = OrganizationGroup.objects.filter(group__name__in=cd['roles'], \
organization=org)
for g in org_groups:
user.groups.add(g.group)
return HttpResponse(reverse('add_user_success'))
template = 'organizations/add_user.html'
template_vars = {'form': form}
# override request with dictionary template_vars
template_vars = FormMediaRequestContext(request=request, dict=template_vars)
return render(request, template, template_vars)
FormMediaEquestContext is a class I import from another file and has its own logic which helps me to handle javascript and css files associated with my form (OrganizationAddUserForm).
create_user_from_manual is yet another function which is encapsulated separately and deals with the reasonably convolutated logic relating to creating a new user in my system and sending an invitation email to that new user.
And of course, I serve up a different template if this is the first time a user arrives on this "add user" page as opposed to redirecting to a completely different url with its own view function and template when the add user form is successfully executed.
By keeping our view functions reasonably small, I have an easier time tracking down bugs relating to specific functionality.
In addition, it is also a good way to "reuse" my utility functions such as the create_user_from_manual method should I need this same utility function in another view function.
However, at the end of the day, organizing code and encapsulating code is a judgement call that you get to make as you progress as a developer.
Someone has probably already developed a technique for relieving the tedium for the following idiomatic unit test:
GET a url with form data already populated
POST a revised form with one or more fields edited
Check response (profit!)
Step 2 is the most tedious, cycling through the form fields. Are there any time-saving hacks for testing Django forms?
[Update: I'm not testing Django forms handling. I'm verifying that my application produces correct responses when a user makes changes to a form. This is an application which processes clinical information, hence a lot of possible responses to test.]
It depends what you are trying to test. I would target your tests a bit more finely than it sounds like you are doing.
If the code you need to test is the form validation logic, then I would simply instantiate the form class directly in your tests, pass it various data dictionaries and call .is_valid(), check for the proper errors or lack thereof. No need to involve HTML or HTTP requests.
If it's view logic (which IMO should be minimized) that you are testing, you will probably want to use the test client, but you shouldn't need to do multi-stage tests or very many tests at this level. In testing view logic I wouldn't scrape HTML (that's testing templates), I'd use response.context to pull out the form object from the context.
If what you want to test is that the templates contain the proper HTML to make the form actually work (in order to catch errors like forgetting to include the management form for a formset in the template), I use WebTest and django-webtest, which parse your HTML and make it easy to fill in field values and submit the form like a browser would.
You can use response.context and form.initial to get the values you need to post:
update_url = reverse('myobject_update',args=(myobject.pk,))
# GET the form
r = self.client.get(update_url)
# retrieve form data as dict
form = r.context['form']
data = form.initial # form is unbound but contains data
# manipulate some data
data['field_to_be_changed'] = 'updated_value'
# POST to the form
r = self.client.post(update_url, data)
# retrieve again
r = self.client.get(update_url)
self.assertContains(r, 'updated_value') # or
self.assertEqual(r.context['form'].initial['field_to_be_changed'], 'updated_value')
django-webtest is perfect for such tests:
from django_webtest import WebTest
class MyTestCase(WebTest):
def test_my_view(self)
form = self.app.get('/my-url/').form
self.assertEqual(form['my_field_10'].value, 'initial value')
form['field_25'] = 'foo'
response = form.submit() # all form fields are submitted
In my opinion it is better than twill for django testing because it provides access to django internals so native django's response.context, response.templates, self.assertTemplateUsed and self.assertFormError API is supported.
On other hand it is better than native django test client because it has much more powerful and easy API.
I'm a bit biased ;) but I believe that django-webtest is now the best way to write django tests.
It's not clear but one guess is that you have tests like this.
class TestSomething( TestCase ):
fixtures = [ "..." ]
def test_field1_should_work( self ):
response= self.client.get( "url with form data already populated" )
form_data = func_to_get_field( response )
form_data['field1']= new value
response= self.client.post( "url", form_data )
self.assert()
def test_field2_should_work( self ):
response= self.client.get( "url with form data already populated" )
form_data = func_to_get_field( response )
form_data['fields']= new value
response= self.client.post( "url", form_data )
self.assert()
First, you're doing too much. Simplify.
class TestFormDefaults( TestCase ):
fixtures = [ "some", "known", "database" ]
def test_get_should_provide_defaults( self ):
response= self.client.get( "url with form data already populated" )
self.assert(...)
The above proves that the defaults populate the forms.
class TestPost( TestCase ):
fixtures = [ "some", "known", "database" ]
def test_field1_should_work( self ):
# No need to GET URL, TestFormDefaults proved that it workd.
form_data= { expected form content based on fixture and previous test }
form_data['field1']= new value
response= self.client.post( "url", form_data )
self.assert()
Don't waste time doing a "get" for each "post". You can prove -- separately -- that the GET operations work. Once you have that proof, simply do the POSTs.
If you POSTS are highly session-specific and stateful, you can still do a GET, but don't bother parsing the response. You can prove (separately) that it has exactly the right fields.
To optimize your resting, consider this.
class TestPost( TestCase ):
fixtures = [ "some", "known", "database" ]
def test_many_changes_should_work( self ):
changes = [
( 'field1', 'someValue', 'some expected response' ),
( 'field2', 'someValue' ),
...
]
for field, value, expected in changes:
self.client.get( "url" ) # doesn't matter what it responds, we've already proven that it works.
form_data= { expected form content based on fixture and previous test }
form_data[field]= value
response self.client.post( "url", form_data )
self.assertEquas( expected, who knows what )
The above will obviously work, but it makes the number of tests appear small.
Think carefully about why you need to unit-test this. Forms are part of the core Django functionality, and as such are very well covered by Django's own unit tests. If all you're doing is basic create/update, which from your question it sounds like is the case, I don't see any reason to write unit tests for that.
I don't see how or why you need unit tests for this. Sounds to me like you're testing for (and correcting) possible user input, which is covered very simply with Django's form validation (and model validation in 1.2)
I'd recommed you to take a look into acceptance testing level tools like robot test framework or letucce which are thought just for you want to do "I'm verifying that my application produces correct responses when a user makes changes to a form", that sounds more like acceptance (black-box) testing than unit-testing.
For instace, Robot let you to define your tests in tabular form, you define the workflow once and then you can define easily a bunch of tests that exercise the workflow with different data.
You are possibly looking for tools that do front end testing like twill or selenium
Both of these generate python code, that can be included within the django tests, so when you run tests, it opens the urls, posts the data and inspects whatever you want!
It should help you to see these tests written for selenium, for an open source reusable django app.