|
@@ -130,22 +130,22 @@ We identify a bug
|
|
|
-----------------
|
|
|
|
|
|
Fortunately, there's a little bug in the ``polls`` application for us to fix
|
|
|
-right away: the ``Poll.was_published_recently()`` method returns ``True`` if
|
|
|
-the ``Poll`` was published within the last day (which is correct) but also if
|
|
|
-the ``Poll``’s ``pub_date`` field is in the future (which certainly isn't).
|
|
|
+right away: the ``Question.was_published_recently()`` method returns ``True`` if
|
|
|
+the ``Question`` was published within the last day (which is correct) but also if
|
|
|
+the ``Question``’s ``pub_date`` field is in the future (which certainly isn't).
|
|
|
|
|
|
-You can see this in the Admin; create a poll whose date lies in the future;
|
|
|
-you'll see that the ``Poll`` change list claims it was published recently.
|
|
|
+You can see this in the Admin; create a question whose date lies in the future;
|
|
|
+you'll see that the ``Question`` change list claims it was published recently.
|
|
|
|
|
|
You can also see this using the shell::
|
|
|
|
|
|
>>> import datetime
|
|
|
>>> from django.utils import timezone
|
|
|
- >>> from polls.models import Poll
|
|
|
- >>> # create a Poll instance with pub_date 30 days in the future
|
|
|
- >>> future_poll = Poll(pub_date=timezone.now() + datetime.timedelta(days=30))
|
|
|
+ >>> from polls.models import Question
|
|
|
+ >>> # create a Question instance with pub_date 30 days in the future
|
|
|
+ >>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
|
|
|
>>> # was it published recently?
|
|
|
- >>> future_poll.was_published_recently()
|
|
|
+ >>> future_question.was_published_recently()
|
|
|
True
|
|
|
|
|
|
Since things in the future are not 'recent', this is clearly wrong.
|
|
@@ -167,20 +167,21 @@ Put the following in the ``tests.py`` file in the ``polls`` application::
|
|
|
from django.utils import timezone
|
|
|
from django.test import TestCase
|
|
|
|
|
|
- from polls.models import Poll
|
|
|
+ from polls.models import Question
|
|
|
|
|
|
- class PollMethodTests(TestCase):
|
|
|
+ class QuestionMethodTests(TestCase):
|
|
|
|
|
|
- def test_was_published_recently_with_future_poll(self):
|
|
|
+ def test_was_published_recently_with_future_question(self):
|
|
|
"""
|
|
|
- was_published_recently() should return False for polls whose
|
|
|
+ was_published_recently() should return False for questions whose
|
|
|
pub_date is in the future
|
|
|
"""
|
|
|
- future_poll = Poll(pub_date=timezone.now() + datetime.timedelta(days=30))
|
|
|
- self.assertEqual(future_poll.was_published_recently(), False)
|
|
|
+ time = timezone.now() + datetime.timedelta(days=30)
|
|
|
+ future_question = Question(pub_date=time)
|
|
|
+ self.assertEqual(future_question.was_published_recently(), False)
|
|
|
|
|
|
What we have done here is created a :class:`django.test.TestCase` subclass
|
|
|
-with a method that creates a ``Poll`` instance with a ``pub_date`` in the
|
|
|
+with a method that creates a ``Question`` instance with a ``pub_date`` in the
|
|
|
future. We then check the output of ``was_published_recently()`` - which
|
|
|
*ought* to be False.
|
|
|
|
|
@@ -196,11 +197,11 @@ and you'll see something like::
|
|
|
Creating test database for alias 'default'...
|
|
|
F
|
|
|
======================================================================
|
|
|
- FAIL: test_was_published_recently_with_future_poll (polls.tests.PollMethodTests)
|
|
|
+ FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionMethodTests)
|
|
|
----------------------------------------------------------------------
|
|
|
Traceback (most recent call last):
|
|
|
- File "/path/to/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_poll
|
|
|
- self.assertEqual(future_poll.was_published_recently(), False)
|
|
|
+ File "/path/to/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_question
|
|
|
+ self.assertEqual(future_question.was_published_recently(), False)
|
|
|
AssertionError: True != False
|
|
|
|
|
|
----------------------------------------------------------------------
|
|
@@ -219,7 +220,7 @@ What happened is this:
|
|
|
|
|
|
* it looked for test methods - ones whose names begin with ``test``
|
|
|
|
|
|
-* in ``test_was_published_recently_with_future_poll`` it created a ``Poll``
|
|
|
+* in ``test_was_published_recently_with_future_question`` it created a ``Question``
|
|
|
instance whose ``pub_date`` field is 30 days in the future
|
|
|
|
|
|
* ... and using the ``assertEqual()`` method, it discovered that its
|
|
@@ -232,14 +233,14 @@ occurred.
|
|
|
Fixing the bug
|
|
|
--------------
|
|
|
|
|
|
-We already know what the problem is: ``Poll.was_published_recently()`` should
|
|
|
+We already know what the problem is: ``Question.was_published_recently()`` should
|
|
|
return ``False`` if its ``pub_date`` is in the future. Amend the method in
|
|
|
``models.py``, so that it will only return ``True`` if the date is also in the
|
|
|
past::
|
|
|
|
|
|
def was_published_recently(self):
|
|
|
now = timezone.now()
|
|
|
- return now - datetime.timedelta(days=1) <= self.pub_date < now
|
|
|
+ return now - datetime.timedelta(days=1) <= self.pub_date < now
|
|
|
|
|
|
and run the test again::
|
|
|
|
|
@@ -269,24 +270,26 @@ introduced another.
|
|
|
Add two more test methods to the same class, to test the behavior of the method
|
|
|
more comprehensively::
|
|
|
|
|
|
- def test_was_published_recently_with_old_poll(self):
|
|
|
+ def test_was_published_recently_with_old_question(self):
|
|
|
"""
|
|
|
- was_published_recently() should return False for polls whose pub_date
|
|
|
- is older than 1 day
|
|
|
+ was_published_recently() should return False for questions whose
|
|
|
+ pub_date is older than 1 day
|
|
|
"""
|
|
|
- old_poll = Poll(pub_date=timezone.now() - datetime.timedelta(days=30))
|
|
|
- self.assertEqual(old_poll.was_published_recently(), False)
|
|
|
+ time = timezone.now() - datetime.timedelta(days=30)
|
|
|
+ old_question = Question(pub_date=time)
|
|
|
+ self.assertEqual(old_question.was_published_recently(), False)
|
|
|
|
|
|
- def test_was_published_recently_with_recent_poll(self):
|
|
|
+ def test_was_published_recently_with_recent_question(self):
|
|
|
"""
|
|
|
- was_published_recently() should return True for polls whose pub_date
|
|
|
- is within the last day
|
|
|
+ was_published_recently() should return True for questions whose
|
|
|
+ pub_date is within the last day
|
|
|
"""
|
|
|
- recent_poll = Poll(pub_date=timezone.now() - datetime.timedelta(hours=1))
|
|
|
- self.assertEqual(recent_poll.was_published_recently(), True)
|
|
|
+ time = timezone.now() - datetime.timedelta(hours=1)
|
|
|
+ recent_question = Question(pub_date=time)
|
|
|
+ self.assertEqual(recent_question.was_published_recently(), True)
|
|
|
|
|
|
-And now we have three tests that confirm that ``Poll.was_published_recently()``
|
|
|
-returns sensible values for past, recent, and future polls.
|
|
|
+And now we have three tests that confirm that ``Question.was_published_recently()``
|
|
|
+returns sensible values for past, recent, and future questions.
|
|
|
|
|
|
Again, ``polls`` is a simple application, but however complex it grows in the
|
|
|
future and whatever other code it interacts with, we now have some guarantee
|
|
@@ -295,9 +298,9 @@ that the method we have written tests for will behave in expected ways.
|
|
|
Test a view
|
|
|
===========
|
|
|
|
|
|
-The polls application is fairly undiscriminating: it will publish any poll,
|
|
|
+The polls application is fairly undiscriminating: it will publish any question,
|
|
|
including ones whose ``pub_date`` field lies in the future. We should improve
|
|
|
-this. Setting a ``pub_date`` in the future should mean that the Poll is
|
|
|
+this. Setting a ``pub_date`` in the future should mean that the Question is
|
|
|
published at that moment, but invisible until then.
|
|
|
|
|
|
A test for a view
|
|
@@ -332,7 +335,7 @@ which will allow us to examine some additional attributes on responses such as
|
|
|
``response.context`` that otherwise wouldn't be available. Note that this
|
|
|
method *does not* setup a test database, so the following will be run against
|
|
|
the existing database and the output may differ slightly depending on what
|
|
|
-polls you already created.
|
|
|
+questions you already created.
|
|
|
|
|
|
Next we need to import the test client class (later in ``tests.py`` we will use
|
|
|
the :class:`django.test.TestCase` class, which comes with its own client, so
|
|
@@ -360,17 +363,17 @@ With that ready, we can ask the client to do some work for us::
|
|
|
>>> # 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 Poll
|
|
|
+ >>> from polls.models import Question
|
|
|
>>> from django.utils import timezone
|
|
|
- >>> # create a Poll and save it
|
|
|
- >>> p = Poll(question="Who is your favorite Beatle?", pub_date=timezone.now())
|
|
|
- >>> p.save()
|
|
|
+ >>> # 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
|
|
|
'\n\n\n <ul>\n \n <li><a href="/polls/1/">Who is your favorite Beatle?</a></li>\n \n </ul>\n\n'
|
|
|
- >>> response.context['latest_poll_list']
|
|
|
- [<Poll: Who is your favorite Beatle?>]
|
|
|
+ >>> response.context['latest_question_list']
|
|
|
+ [<Question: Who is your favorite Beatle?>]
|
|
|
|
|
|
Improving our view
|
|
|
------------------
|
|
@@ -383,13 +386,13 @@ based on :class:`~django.views.generic.list.ListView`::
|
|
|
|
|
|
class IndexView(generic.ListView):
|
|
|
template_name = 'polls/index.html'
|
|
|
- context_object_name = 'latest_poll_list'
|
|
|
+ context_object_name = 'latest_question_list'
|
|
|
|
|
|
def get_queryset(self):
|
|
|
- """Return the last five published polls."""
|
|
|
- return Poll.objects.order_by('-pub_date')[:5]
|
|
|
+ """Return the last five published questions."""
|
|
|
+ return Question.objects.order_by('-pub_date')[:5]
|
|
|
|
|
|
-``response.context_data['latest_poll_list']`` extracts the data this view
|
|
|
+``response.context_data['latest_question_list']`` extracts the data this view
|
|
|
places into the context.
|
|
|
|
|
|
We need to amend the ``get_queryset`` method and change it so that it also
|
|
@@ -402,24 +405,24 @@ and then we must amend the ``get_queryset`` method like so::
|
|
|
|
|
|
def get_queryset(self):
|
|
|
"""
|
|
|
- Return the last five published polls (not including those set to be
|
|
|
+ Return the last five published questions (not including those set to be
|
|
|
published in the future).
|
|
|
"""
|
|
|
- return Poll.objects.filter(
|
|
|
+ return Question.objects.filter(
|
|
|
pub_date__lte=timezone.now()
|
|
|
).order_by('-pub_date')[:5]
|
|
|
|
|
|
-``Poll.objects.filter(pub_date__lte=timezone.now())`` returns a queryset
|
|
|
-containing Polls whose ``pub_date`` is less than or equal to - that is, earlier
|
|
|
-than or equal to - ``timezone.now``.
|
|
|
+``Question.objects.filter(pub_date__lte=timezone.now())`` returns a queryset
|
|
|
+containing ``Question``\s whose ``pub_date`` is less than or equal to - that
|
|
|
+is, earlier than or equal to - ``timezone.now``.
|
|
|
|
|
|
Testing our new view
|
|
|
--------------------
|
|
|
|
|
|
Now you can satisfy yourself that this behaves as expected by firing up the
|
|
|
-runserver, loading the site in your browser, creating ``Polls`` with dates in
|
|
|
-the past and future, and checking that only those that have been published are
|
|
|
-listed. You don't want to have to do that *every single time you make any
|
|
|
+runserver, loading the site in your browser, creating ``Questions`` with dates
|
|
|
+in the past and future, and checking that only those that have been published
|
|
|
+are listed. You don't want to have to do that *every single time you make any
|
|
|
change that might affect this* - so let's also create a test, based on our
|
|
|
shell session above.
|
|
|
|
|
@@ -427,91 +430,98 @@ Add the following to ``polls/tests.py``::
|
|
|
|
|
|
from django.core.urlresolvers import reverse
|
|
|
|
|
|
-and we'll create a factory method to create polls as well as a new test class::
|
|
|
+and we'll create a factory method to create questions as well as a new test
|
|
|
+class::
|
|
|
|
|
|
- def create_poll(question, days):
|
|
|
+ def create_question(question_text, days):
|
|
|
"""
|
|
|
- Creates a poll with the given `question` published the given number of
|
|
|
- `days` offset to now (negative for polls published in the past,
|
|
|
- positive for polls that have yet to be published).
|
|
|
+ Creates a question with the given `question_text` published the given
|
|
|
+ number of `days` offset to now (negative for questions published
|
|
|
+ in the past, positive for questions that have yet to be published).
|
|
|
"""
|
|
|
- return Poll.objects.create(question=question,
|
|
|
- pub_date=timezone.now() + datetime.timedelta(days=days))
|
|
|
+ time = timezone.now() + datetime.timedelta(days=days)
|
|
|
+ return Question.objects.create(question_text=question_text,
|
|
|
+ pub_date=time)
|
|
|
|
|
|
- class PollViewTests(TestCase):
|
|
|
- def test_index_view_with_no_polls(self):
|
|
|
+
|
|
|
+ class QuestionViewTests(TestCase):
|
|
|
+ def test_index_view_with_no_questions(self):
|
|
|
"""
|
|
|
- If no polls exist, an appropriate message should be displayed.
|
|
|
+ If no questions exist, an appropriate message should be displayed.
|
|
|
"""
|
|
|
response = self.client.get(reverse('polls:index'))
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
self.assertContains(response, "No polls are available.")
|
|
|
- self.assertQuerysetEqual(response.context['latest_poll_list'], [])
|
|
|
+ self.assertQuerysetEqual(response.context['latest_question_list'], [])
|
|
|
|
|
|
- def test_index_view_with_a_past_poll(self):
|
|
|
+ def test_index_view_with_a_past_question(self):
|
|
|
"""
|
|
|
- Polls with a pub_date in the past should be displayed on the index page.
|
|
|
+ Questions with a pub_date in the past should be displayed on the
|
|
|
+ index page
|
|
|
"""
|
|
|
- create_poll(question="Past poll.", days=-30)
|
|
|
+ create_question(question_text="Past question.", days=-30)
|
|
|
response = self.client.get(reverse('polls:index'))
|
|
|
self.assertQuerysetEqual(
|
|
|
- response.context['latest_poll_list'],
|
|
|
- ['<Poll: Past poll.>']
|
|
|
+ response.context['latest_question_list'],
|
|
|
+ ['<Question: Past question.>']
|
|
|
)
|
|
|
|
|
|
- def test_index_view_with_a_future_poll(self):
|
|
|
+ def test_index_view_with_a_future_question(self):
|
|
|
"""
|
|
|
- Polls with a pub_date in the future should not be displayed on the
|
|
|
- index page.
|
|
|
+ Questions with a pub_date in the future should not be displayed on
|
|
|
+ the index page.
|
|
|
"""
|
|
|
- create_poll(question="Future poll.", days=30)
|
|
|
+ create_question(question_text="Future question.", days=30)
|
|
|
response = self.client.get(reverse('polls:index'))
|
|
|
- self.assertContains(response, "No polls are available.", status_code=200)
|
|
|
- self.assertQuerysetEqual(response.context['latest_poll_list'], [])
|
|
|
+ self.assertContains(response, "No polls are available.",
|
|
|
+ status_code=200)
|
|
|
+ self.assertQuerysetEqual(response.context['latest_question_list'], [])
|
|
|
|
|
|
- def test_index_view_with_future_poll_and_past_poll(self):
|
|
|
+ def test_index_view_with_future_question_and_past_question(self):
|
|
|
"""
|
|
|
- Even if both past and future polls exist, only past polls should be
|
|
|
- displayed.
|
|
|
+ Even if both past and future questions exist, only past questions
|
|
|
+ should be displayed.
|
|
|
"""
|
|
|
- create_poll(question="Past poll.", days=-30)
|
|
|
- create_poll(question="Future poll.", days=30)
|
|
|
+ create_question(question_text="Past question.", days=-30)
|
|
|
+ create_question(question_text="Future question.", days=30)
|
|
|
response = self.client.get(reverse('polls:index'))
|
|
|
self.assertQuerysetEqual(
|
|
|
- response.context['latest_poll_list'],
|
|
|
- ['<Poll: Past poll.>']
|
|
|
+ response.context['latest_question_list'],
|
|
|
+ ['<Question: Past question.>']
|
|
|
)
|
|
|
|
|
|
- def test_index_view_with_two_past_polls(self):
|
|
|
+ def test_index_view_with_two_past_questions(self):
|
|
|
"""
|
|
|
- The polls index page may display multiple polls.
|
|
|
+ The questions index page may display multiple questions.
|
|
|
"""
|
|
|
- create_poll(question="Past poll 1.", days=-30)
|
|
|
- create_poll(question="Past poll 2.", days=-5)
|
|
|
+ create_question(question_text="Past quesiton 1.", days=-30)
|
|
|
+ create_question(question_text="Past question 2.", days=-5)
|
|
|
response = self.client.get(reverse('polls:index'))
|
|
|
self.assertQuerysetEqual(
|
|
|
- response.context['latest_poll_list'],
|
|
|
- ['<Poll: Past poll 2.>', '<Poll: Past poll 1.>']
|
|
|
+ response.context['latest_question_list'],
|
|
|
+ ['<Question: Past question 2.>', '<Question: Past question 1.>']
|
|
|
)
|
|
|
|
|
|
+
|
|
|
Let's look at some of these more closely.
|
|
|
|
|
|
-First is a poll factory method, ``create_poll``, to take some repetition out
|
|
|
-of the process of creating polls.
|
|
|
+First is a question factory method, ``create_question``, to take some
|
|
|
+repetition out of the process of creating questions.
|
|
|
|
|
|
-``test_index_view_with_no_polls`` doesn't create any polls, but checks the
|
|
|
-message: "No polls are available." and verifies the ``latest_poll_list`` is
|
|
|
-empty. Note that the :class:`django.test.TestCase` class provides some
|
|
|
+``test_index_view_with_no_questions`` doesn't create any questions, but checks
|
|
|
+the message: "No polls are available." and verifies the ``latest_question_list``
|
|
|
+is empty. Note that the :class:`django.test.TestCase` class provides some
|
|
|
additional assertion methods. In these examples, we use
|
|
|
:meth:`~django.test.SimpleTestCase.assertContains()` and
|
|
|
:meth:`~django.test.TransactionTestCase.assertQuerysetEqual()`.
|
|
|
|
|
|
-In ``test_index_view_with_a_past_poll``, we create a poll and verify that it
|
|
|
+In ``test_index_view_with_a_past_question``, we create a question and verify that it
|
|
|
appears in the list.
|
|
|
|
|
|
-In ``test_index_view_with_a_future_poll``, we create a poll with a ``pub_date``
|
|
|
-in the future. The database is reset for each test method, so the first poll is
|
|
|
-no longer there, and so again the index shouldn't have any polls in it.
|
|
|
+In ``test_index_view_with_a_future_question``, we create a question with a
|
|
|
+``pub_date`` in the future. The database is reset for each test method, so the
|
|
|
+first question is no longer there, and so again the index shouldn't have any
|
|
|
+questions in it.
|
|
|
|
|
|
And so on. In effect, we are using the tests to tell a story of admin input
|
|
|
and user experience on the site, and checking that at every state and for every
|
|
@@ -520,41 +530,47 @@ new change in the state of the system, the expected results are published.
|
|
|
Testing the ``DetailView``
|
|
|
--------------------------
|
|
|
|
|
|
-What we have works well; however, even though future polls don't appear in the
|
|
|
-*index*, users can still reach them if they know or guess the right URL. So we
|
|
|
-need to add a similar constraint to ``DetailView``::
|
|
|
+What we have works well; however, even though future questions don't appear in
|
|
|
+the *index*, users can still reach them if they know or guess the right URL. So
|
|
|
+we need to add a similar constraint to ``DetailView``::
|
|
|
|
|
|
|
|
|
class DetailView(generic.DetailView):
|
|
|
...
|
|
|
def get_queryset(self):
|
|
|
"""
|
|
|
- Excludes any polls that aren't published yet.
|
|
|
+ Excludes any questions that aren't published yet.
|
|
|
"""
|
|
|
- return Poll.objects.filter(pub_date__lte=timezone.now())
|
|
|
+ return Question.objects.filter(pub_date__lte=timezone.now())
|
|
|
|
|
|
-And of course, we will add some tests, to check that a ``Poll`` whose
|
|
|
+And of course, we will add some tests, to check that a ``Question`` whose
|
|
|
``pub_date`` is in the past can be displayed, and that one with a ``pub_date``
|
|
|
in the future is not::
|
|
|
|
|
|
- class PollIndexDetailTests(TestCase):
|
|
|
- def test_detail_view_with_a_future_poll(self):
|
|
|
+ class QuestionIndexDetailTests(TestCase):
|
|
|
+ def test_detail_view_with_a_future_question(self):
|
|
|
"""
|
|
|
- The detail view of a poll with a pub_date in the future should
|
|
|
+ The detail view of a question with a pub_date in the future should
|
|
|
return a 404 not found.
|
|
|
"""
|
|
|
- future_poll = create_poll(question='Future poll.', days=5)
|
|
|
- response = self.client.get(reverse('polls:detail', args=(future_poll.id,)))
|
|
|
+ future_question = create_question(question_text='Future question.',
|
|
|
+ days=5)
|
|
|
+ response = self.client.get(reverse('polls:detail',
|
|
|
+ args=(future_question.id,)))
|
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
|
|
- def test_detail_view_with_a_past_poll(self):
|
|
|
+ def test_detail_view_with_a_past_question(self):
|
|
|
"""
|
|
|
- The detail view of a poll with a pub_date in the past should display
|
|
|
- the poll's question.
|
|
|
+ The detail view of a question with a pub_date in the past should
|
|
|
+ display the question's text.
|
|
|
"""
|
|
|
- past_poll = create_poll(question='Past Poll.', days=-5)
|
|
|
- response = self.client.get(reverse('polls:detail', args=(past_poll.id,)))
|
|
|
- self.assertContains(response, past_poll.question, status_code=200)
|
|
|
+ past_question = create_question(question_text='Past Question.',
|
|
|
+ days=-5)
|
|
|
+ response = self.client.get(reverse('polls:detail',
|
|
|
+ args=(past_question.id,)))
|
|
|
+ self.assertContains(response, past_question.question_text,
|
|
|
+ status_code=200)
|
|
|
+
|
|
|
|
|
|
Ideas for more tests
|
|
|
--------------------
|
|
@@ -564,17 +580,17 @@ create a new test class for that view. It'll be very similar to what we have
|
|
|
just created; in fact there will be a lot of repetition.
|
|
|
|
|
|
We could also improve our application in other ways, adding tests along the
|
|
|
-way. For example, it's silly that ``Polls`` can be published on the site that
|
|
|
-have no ``Choices``. So, our views could check for this, and exclude such
|
|
|
-``Polls``. Our tests would create a ``Poll`` without ``Choices`` and then test
|
|
|
-that it's not published, as well as create a similar ``Poll`` *with*
|
|
|
-``Choices``, and test that it *is* published.
|
|
|
-
|
|
|
-Perhaps logged-in admin users should be allowed to see unpublished ``Polls``,
|
|
|
-but not ordinary visitors. Again: whatever needs to be added to the software to
|
|
|
-accomplish this should be accompanied by a test, whether you write the test
|
|
|
-first and then make the code pass the test, or work out the logic in your code
|
|
|
-first and then write a test to prove it.
|
|
|
+way. For example, it's silly that ``Questions`` can be published on the site
|
|
|
+that have no ``Choices``. So, our views could check for this, and exclude such
|
|
|
+``Questions``. Our tests would create a ``Question`` without ``Choices`` and
|
|
|
+then test that it's not published, as well as create a similar ``Question``
|
|
|
+*with* ``Choices``, and test that it *is* published.
|
|
|
+
|
|
|
+Perhaps logged-in admin users should be allowed to see unpublished
|
|
|
+``Questions``, but not ordinary visitors. Again: whatever needs to be added to
|
|
|
+the software to accomplish this should be accompanied by a test, whether you
|
|
|
+write the test first and then make the code pass the test, or work out the
|
|
|
+logic in your code first and then write a test to prove it.
|
|
|
|
|
|
At a certain point you are bound to look at your tests and wonder whether your
|
|
|
code is suffering from test bloat, which brings us to:
|
|
@@ -591,7 +607,7 @@ once and then forget about it. It will continue performing its useful function
|
|
|
as you continue to develop your program.
|
|
|
|
|
|
Sometimes tests will need to be updated. Suppose that we amend our views so that
|
|
|
-only ``Polls`` with ``Choices`` are published. In that case, many of our
|
|
|
+only ``Questions`` with ``Choices`` are published. In that case, many of our
|
|
|
existing tests will fail - *telling us exactly which tests need to be amended to
|
|
|
bring them up to date*, so to that extent tests help look after themselves.
|
|
|
|