In this part, we will look further into testing (how to test views, using the test client, and general testing tips).
Previously, we wrote a test to make sure that our was_published_recently() method would return False if the publication date is in the future. However, the view that shows the questions does not currently check the publication date. We need to test the view to see how an end user would experience this problem.
The Django Test Client
Django provides you with a client that we can use in the shell or within the test.py file. This client is used to simulate a user interacting with the code at the view level. This is like programmatically clicking on links on your app and seeing what gets returned.
Open the shell (remember, navigate to your manage.py file then type the command “python manage.py shell” to open the shell).
Next we need to setup the test environment (note that doing it in the test.py file will differ slightly from this). To do this, type:
from django.test.utils import setup_test_environment setup_test_environment()
The setup_test_environment() function installs a template renderer. This will let us look at additional attributes on responses that otherwise wouldn’t be available. This doesn’t, however, setup a test database.
Now, we need import the test client class:
from django.test import Client client = Client()
This creates an instance of the client and it will let us do some work. Try out some of these commands:
# get a response from '/' response = client.get('/') # we should expect a 404 from that address response.status_code 404 # on the other hand we should expect to find something at '/polls/' # we'll use 'reverse()' rather than a hardcoded URL from django.core.urlresolvers import reverse response = client.get(reverse('polls:index')) response.status_code 200 response.content b'\n\n\n <p>No polls are available.</p>\n\n' # note - you might get unexpected results if your ``TIME_ZONE`` # in ``settings.py`` is not correct. If you need to change it, # you will also need to restart your shell session from polls.models import Question from django.utils import timezone # create a Question and save it q = Question(question_text="Who is your favorite Beatle?", pub_date=timezone.now()) q.save() # check the response once again response = client.get('/polls/') response.content b'\n\n\n <ul>\n \n <li><a href="/polls/1/">Who is your favorite Beatle?</a></li>\n \n </ul>\n\n' # If the following doesn't work, you probably omitted the call to # setup_test_environment() described above response.context['latest_question_list'] [<Question: Who is your favorite Beatle?>]
Fix our View
We don’t want the list of polls to show pols that aren’t yet published. Open the polls/views.py file. We want to update the get_queryset() method within the IndexView class.
Add the following import to the beginning of the file:
from django.utils import timezone
Next, update the get_queryset() method to be this:
def get_queryset(self): """ Return the last five published questions (not including those set to be published in the future). """ return Question.objects.filter( pub_date__lte=timezone.now() ).order_by('-pub_date')[:5]
Test The View
There are two ways we can test this. First, we can load up the runserver and the site in our browser then add a new question with a publication date in the future to see if it will be displayed. However, we might want to do this test many times in the future. A better way to do this test is programmatically.
When I first ran these tests, I got an error that said
u'polls' is not a registered namespace
This occurred because I didn’t include the ‘polls’ namespace in the root urlconf file. To fix this, open your /app/urls.py file (for me, this is /pantrio/urls.py) and change the line
to the following (so that it knows we are looking at the polls namespace):
We need to do this because in the /polls/urls.py file we added the lines:
app_name = 'polls' urlpatterns = [ ... ]
Beyond that, the tests should run fine.
Other Items to Test
If you want to test your own ability to write tests and improve views, you can go through a similar methodology with the ResultsView. You might also want to test to make sure that a Question always has at least one (or maybe even at least two) Choice(s).
Is There Such A Thing as Too Much Testing?
There are many more things we can test. Pretty soon, we might start to feel like we are writing more tests than actual code. You might even start to worry that you are suffering from ‘test bloat’. However, in general, since we pretty much write one test and then forget about it, it doesn’t matter if your list of tests grows.
That doesn’t mean, though, that your tests will never change. As your code changes, you may find that you need to update your previous tests. That’s okay, too.
Also, as your app and tests continue to grow, we might find that our tests are redundant. Even this is okay – testing redundancy is typically a good thing. A big idea to keep in mind is that we want our tests organized, since we know they will likely grow. Some rules of thumb are to have:
- a separate TestClass for each model or view
- a separate test method for each set of conditions you want to test
- test method names that describe their function and are easy to understand
Next time, we will look at updating the feel of our app. I am getting really excited about trying to implement these ideas for my own application!
Have questions or suggestions? Please feel free to comment below or contact me.