Test again:
1 Creating test database for alias 'default'...
2 ..
3
---4 Ran 2 tests in 0.759s
5
6 OK
Testing Templates and Views
The issue with the last test is that even if you’re returning the incorrect HTML, it will still pass.
First test: template
It’s better to verify the actual template used:
1 def test_uses_index_html_template(self):
2 index = self.client.get('/')
3 self.assertTemplateUsed(index, "index.html")
So,assertTemplateUsed()is one of those helper function provided by Django, which just checks to see if aHTTPResponseobject gets generated by a specific template. Check out the Djangodocsfor more info.
Second test: expected HTML
We can further increase our test coverage to verify that not only are we using the appropri-ate templappropri-ate but that the HTML being returned is correct as well. In other words, after the template engine has done its thing, do we get the expected HTML?
The test looks like this:
1 from django.shortcuts import render_to_response
2
In the above test, the render_to_response() shortcut function is used to run our in-dex.htmltemplate through the Django template engine and ensure that the response is the same had an end user called the/url.
Third test: expected HTML redux
The final thing to test for theindexview is that it returns the appropriate html for a logged-in user. This is actually a bit trickier than it sounds, because theindex()function actually performs three separate functions:
1. It checks the session to see if a user is logged in.
2. If the user is logged in, it queries the database to get that user’s information.
3. Finally, it returns the appropriate HTTP Response.
Now is the time that we may want to argue for refactoring the code into multiple functions to make it easier to test. But for now let’s assume we aren’t going to refactor and we want to test the functionality as is. This means that really we are executing aIntegration Testinstead of aUnit Test, because we are now testing several parts of the application as opposed to just one individual unit.
NOTE: Notice how during this process we found an issue in our code - e.g., one function having too many responsibilities. This is a side effect of testing: You learn to write better code.
Forgetting about the distinction for the time being, let’s look at how we might test this func-tionality:
1. Create a dummy session with theuserentry that we need.
2. Ensure there is a user in the database, so our lookup works.
3. Verify that we get the appropriate response back.
Let’s work backwards: …
1 def test_index_handles_logged_in_user(self):
2
3 # test logic will go here
4
5 self.assertTemplateUsed(resp, 'user.html')
We have seen this before: See the first test in this section,test_uses_index_html_template(). Above we are just trying to verify that when there is a logged in user, we return theuser.html template instead of the normalindex.htmltemplate.
1 def test_index_handles_logged_in_user(self):
2 # create the user needed for user lookup from index page
3 from payments.models import User
4 user = User(
5 name='jj',
6 email='[email protected]',
7 )
8 user.save()
9
10 ...snipped code...
11
12 # verify the response returns the page for the logged in user
13 self.assertTemplateUsed(resp, 'user.html')
Here we are creating a user and storing that user in the database. This is more or less the same thing we do when a new user registers.
Take a breather Before continuing, be sure you understand exactly what happens when you run a Django unittest. Flip back to the last chapter if you need a quick refresher.
Do you understand why we need to create a user to test this function correctly if our main.views.index()function is working? Yes? Move on. No? Flip back and read about thedjango.test.TestCase(), focusing specifically on how it handles the database layer.
Ready?
Move on Want the answer? It’s simple: It’s because thedjango.test.TestCaseclass that we are inheriting from will clear out the database prior to each run. Make sense?
Also, you may not have noticed, but we are testing against the actual database. This is prob-lematic, but there is a solution. Sort of. We will get to this later. But first, think about the request object that is passed into the view. Here is the view code we are testing:
1 def index(request):
As you can see, the view depends upon a request object and a session. Usually these are managed by the web browser and will just be there, but in the context of unit testing they won’tjust be there. We need a way to make the testthink there is a request object with a session attached.
And there is a technique in unit testing for doing this.
Mocks, fakes, test doubles, dummy objects, stubs There are actually many names for this technique: mocking,faking, usingdummy objects,test doubling, stubbing, etc.
There are subtle difference between each of these, but the terminology just adds confusion.
For the most part, they are all referring to the same thing - which we will define as: Using a temporary, in-memory object to simulate a real object for the purposes of decreasing test complexity and increasing test execution speed.
Visually you can think of it as:
Figure 3.2: mocking example
As you can see from the illustration, amock(or whatever you want to call it) simply takes the place of a real object allowing you to create tests that don’t rely on external dependencies.
This is advantageous for two main reasons:
1. Without external dependencies your tests often run much faster; this is important be-cause unit tests should be ran frequently (ideally after every code change).
2. Without external dependencies it’s much simpler to manage the necessary state for a test, keeping the test simple and easy to understand.
Coming back to the request and its state - the user value stored in the session - we can mock out this external dependency by using Django’sbuilt-inRequestFactory():
1 # create a dummy request
2 from django.test import RequestFactory
3 request_factory = RequestFactory()
4 request = request_factory.get('/')
5 request.session = {} # make sure it has an associated session
By creating the request mock, we can set the state (i.e. our session values) to whatever we want and simply write and execute unit tests to ensure that our view function correctly responds to the session state.
Step 1: Verify that we get the appropriate response back And with that we can com-plete our earlier unit test:
1 from payment.models import User
2 from django.test import RequestFactory
3
4 def test_index_handles_logged_in_user(self):
5 #create the user needed for user lookup from index page
6 from payments.models import User
7 user = User(
13 #create a Mock request object, so we can manipulate the session
14 request_factory = RequestFactory()
15 request = request_factory.get('/')
16 #create a session that appears to have a logged in user
17 request.session = {"user": "1"}
18
19 #request the index page
20 resp = index(request)
21
22 #verify it returns the page for the logged in user
23 self.assertEquals(
24 resp.content,
25 render_to_response('user.html', {'user': user}).content
26 )
Run the tests:
1 Creating test database for alias 'default'...
2 ....
3
---4 Ran 4 tests in 0.964s
5
6 OK
7 Destroying test database for alias 'default'...
Good.
Let’s recap
Ourtest_index_handles_logged_in_user()test:
1. First creates a user in the database.
2. Then we mock the request object and set the session to have a userwith anidof1 (the user we just created). In other words, we are setting the (initial) state so we can test that the index view responds correctly when we have a logged-in user.
3. Then we call the index view function, passing in our mock request then verifying that it returns the appropriate HTML.
At this point we have the test completed for our main app. You may want to review the tests.
Would you do anything different? Experiment. Challenge yourself. There’s always better, more efficient ways of accomplishing a task. Learn how to fail. Struggle. Ask questions.
Experience a few wins. Then learn how to succeed.
Before we moving to testing the models, we should have a look at refactoring some of our