Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Finish rewriting tutorial 5

  • Loading branch information...
commit 01a12be2e9a3744a8a31c2a4dee77cd81c81a782 1 parent 64ae7bd
@hjwp authored
View
155 mysite/fts/test_polls.py
@@ -1,155 +0,0 @@
-from functional_tests import FunctionalTest, ROOT
-from selenium.webdriver.common.keys import Keys
-from collections import namedtuple
-
-PollInfo = namedtuple('PollInfo', ['question', 'choices'])
-POLL1 = PollInfo(
- question="How awesome is Test-Driven Development?",
- choices=[
- 'Very awesome',
- 'Quite awesome',
- 'Moderately awesome',
- ],
-)
-POLL2 = PollInfo(
- question="Which workshop treat do you prefer?",
- choices=[
- 'Beer',
- 'Pizza',
- 'The Acquisition of Knowledge',
- ],
-)
-
-class TestPolls(FunctionalTest):
- def _setup_polls_via_admin(self):
- # Gertrude logs into the admin site
- self.browser.get(ROOT + '/admin/')
- username_field = self.browser.find_element_by_name('username')
- username_field.send_keys('admin')
- password_field = self.browser.find_element_by_name('password')
- password_field.send_keys('adm1n')
- password_field.send_keys(Keys.RETURN)
-
- # She has a number of polls to enter. For each one, she:
- for poll_info in [POLL1, POLL2]:
- # Follows the link to the Polls app, and adds a new Poll
- self.browser.find_elements_by_link_text('Polls')[1].click()
- self.browser.find_element_by_link_text('Add poll').click()
-
- # Enters its name, and uses the 'today' and 'now' buttons to set
- # the publish date
- question_field = self.browser.find_element_by_name('question')
- question_field.send_keys(poll_info.question)
- self.browser.find_element_by_link_text('Today').click()
- self.browser.find_element_by_link_text('Now').click()
-
- # Sees she can enter choices for the Poll on this same page,
- # so she does
- for i, choice_text in enumerate(poll_info.choices):
- choice_field = self.browser.find_element_by_name('choice_set-%d-choice' % i)
- choice_field.send_keys(choice_text)
-
- # Saves her new poll
- save_button = self.browser.find_element_by_css_selector("input[value='Save']")
- save_button.click()
-
- # Is returned to the "Polls" listing, where she can see her
- # new poll, listed as a clickable link by its name
- new_poll_links = self.browser.find_elements_by_link_text(
- poll_info.question
- )
- self.assertEquals(len(new_poll_links), 1)
-
- # She goes back to the root of the admin site
- self.browser.get(ROOT + '/admin/')
-
- # She logs out of the admin site
- self.browser.find_element_by_link_text('Log out').click()
-
-
- def test_voting_on_a_new_poll(self):
- # First, Gertrude the administrator logs into the admin site and
- # creates a couple of new Polls, and their response choices
- self._setup_polls_via_admin()
-
- # Now, Herbert the regular user goes to the homepage of the site. He
- # sees a list of polls.
- self.browser.get(ROOT)
- heading = self.browser.find_element_by_tag_name('h1')
- self.assertEquals(heading.text, 'Polls')
-
- # He clicks on the link to the first Poll, which is called
- # 'How awesome is test-driven development?'
- first_poll_title = 'How awesome is Test-Driven Development?'
- self.browser.find_element_by_link_text(first_poll_title).click()
-
- # He is taken to a poll 'results' page, which says
- # "no-one has voted on this poll yet"
- main_heading = self.browser.find_element_by_tag_name('h1')
- self.assertEquals(main_heading.text, 'Poll Results')
- sub_heading = self.browser.find_element_by_tag_name('h2')
- self.assertEquals(sub_heading.text, first_poll_title)
- body = self.browser.find_element_by_tag_name('body')
- self.assertIn('No-one has voted on this poll yet', body.text)
-
- # He also sees a form, which offers him several choices.
- # There are three options with radio buttons
- choice_inputs = self.browser.find_elements_by_css_selector(
- "input[type='radio']"
- )
- self.assertEquals(len(choice_inputs), 3)
-
- # The buttons have labels to explain them
- choice_labels = self.browser.find_elements_by_tag_name('label')
- choices_text = [c.text for c in choice_labels]
- self.assertEquals(choices_text, [
- 'Vote:', # this label is auto-generated for the whole form
- 'Very awesome',
- 'Quite awesome',
- 'Moderately awesome',
- ])
- # He decided to select "very awesome", which is answer #1
- chosen = self.browser.find_element_by_css_selector(
- "input[value='1']"
- )
- chosen.click()
-
- # Herbert clicks 'submit'
- self.browser.find_element_by_css_selector(
- "input[type='submit']"
- ).click()
-
- # The page refreshes, and he sees that his choice has updated the
- # results. they now say # "100 %: very awesome".
- body_text = self.browser.find_element_by_tag_name('body').text
- self.assertIn('100 %: Very awesome', body_text)
-
- # The page also says "1 vote"
- self.assertIn('1 vote', body_text)
-
- # Harold suspects that the website isn't very well protected
- # against people submitting multiple votes yet, so he tries
- # to do a little astroturfing
- self.browser.find_element_by_css_selector("input[value='1']").click()
- self.browser.find_element_by_css_selector("input[type='submit']").click()
-
- # The page refreshes, and he sees that his choice has updated the
- # results. it still says # "100 %: very awesome".
- body_text = self.browser.find_element_by_tag_name('body').text
- self.assertIn('100 %: Very awesome', body_text)
-
- # But the page now says "2 votes"
- self.assertIn('2 votes', body_text)
-
- # Cackling manically over his l33t haxx0ring skills, he tries
- # voting for a different choice
- self.browser.find_element_by_css_selector("input[value='2']").click()
- self.browser.find_element_by_css_selector("input[type='submit']").click()
-
- # Now, the percentages update, as well as the votes
- body_text = self.browser.find_element_by_tag_name('body').text
- self.assertIn('67 %: Very awesome', body_text)
- self.assertIn('33 %: Quite awesome', body_text)
- self.assertIn('3 votes', body_text)
-
- # Satisfied, he goes back to sleep
View
36 mysite/fts/tests.py
@@ -218,9 +218,39 @@ def test_voting_on_a_new_poll(self):
# The page refreshes, and he sees that his choice
# has updated the results. they now say
# "100 %: very awesome".
- self.fail('TODO')
-
- # The page also says "1 votes"
+ body_text = self.browser.find_element_by_tag_name('body').text
+ self.assertIn('100 %: Very awesome', body_text)
+
+ # The page also says "1 vote"
+ self.assertIn('1 vote', body_text)
+
+ # But not "1 votes" -- Herbert is impressed at the attention to detail
+ self.assertNotIn('1 votes', body_text)
+
+ # Herbert suspects that the website isn't very well protected
+ # against people submitting multiple votes yet, so he tries
+ # to do a little astroturfing
+ self.browser.find_element_by_css_selector("input[value='1']").click()
+ self.browser.find_element_by_css_selector("input[type='submit']").click()
+
+ # The page refreshes, and he sees that his choice has updated the
+ # results. it still says # "100 %: very awesome".
+ body_text = self.browser.find_element_by_tag_name('body').text
+ self.assertIn('100 %: Very awesome', body_text)
+
+ # But the page now says "2 votes"
+ self.assertIn('2 votes', body_text)
+
+ # Cackling manically over his l33t haxx0ring skills, he tries
+ # voting for a different choice
+ self.browser.find_element_by_css_selector("input[value='2']").click()
+ self.browser.find_element_by_css_selector("input[type='submit']").click()
+
+ # Now, the percentages update, as well as the votes
+ body_text = self.browser.find_element_by_tag_name('body').text
+ self.assertIn('67 %: Very awesome', body_text)
+ self.assertIn('33 %: Quite awesome', body_text)
+ self.assertIn('3 votes', body_text)
# Satisfied, he goes back to sleep
View
11 mysite/polls/models.py
@@ -7,7 +7,18 @@ class Poll(models.Model):
def __unicode__(self):
return self.question
+ def total_votes(self):
+ return sum(c.votes for c in self.choice_set.all())
+
+
+
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
+
+ def percentage(self):
+ try:
+ return 100.0 * self.votes / self.poll.total_votes()
+ except ZeroDivisionError:
+ return 0
View
19 mysite/polls/templates/poll.html
@@ -4,11 +4,24 @@
<h2>{{poll.question}}</h2>
- <p>No-one has voted on this poll yet</p>
+ <ul>
+ {% for choice in poll.choice_set.all %}
+ <li>{{ choice.percentage|floatformat:0 }} %: {{ choice.choice }}</li>
+ {% endfor %}
+ </ul>
+
+ {% if poll.total_votes != 0 %}
+ <p>{{ poll.total_votes }} vote{{ poll.total_votes|pluralize }}</p>
+ {% else %}
+ <p>No-one has voted on this poll yet</p>
+ {% endif %}
<h3>Add your vote</h3>
- {{form.as_p}}
- <input type="submit" />
+ <form method="POST" action="">
+ {% csrf_token %}
+ {{form.as_p}}
+ <input type="submit" />
+ </form>
</body>
</html>
View
188 mysite/polls/tests.py
@@ -1,188 +0,0 @@
-from django.core.urlresolvers import reverse
-from django.test import TestCase
-from django.utils import timezone
-from polls.models import Choice, Poll
-
-class TestPollsModel(TestCase):
- def test_creating_a_new_poll_and_saving_it_to_the_database(self):
- # start by creating a new Poll object with its "question" and
- # "pub_date" attributes set
- poll = Poll()
- poll.question = "What's up?"
- poll.pub_date = timezone.now()
-
- # check we can save it to the database
- poll.save()
-
- # now check we can find it in the database again
- all_polls_in_database = Poll.objects.all()
- self.assertEquals(len(all_polls_in_database), 1)
- only_poll_in_database = all_polls_in_database[0]
- self.assertEquals(only_poll_in_database, poll)
-
- # and check that it's saved its two attributes: question and pub_date
- self.assertEquals(only_poll_in_database.question, "What's up?")
- self.assertEquals(only_poll_in_database.pub_date, poll.pub_date)
-
-
- def test_verbose_name_for_pub_date(self):
- for field in Poll._meta.fields:
- if field.name == 'pub_date':
- self.assertEquals(field.verbose_name, 'Date published')
-
-
- def test_poll_objects_are_named_after_their_question(self):
- p = Poll()
- p.question = 'How is babby formed?'
- self.assertEquals(unicode(p), 'How is babby formed?')
-
-
-
-class TestPollChoicesModel(TestCase):
-
- def test_creating_some_choices_for_a_poll(self):
- # start by creating a new Poll object
- poll = Poll()
- poll.question="What's up?"
- poll.pub_date = timezone.now()
- poll.save()
-
- # now create a Choice object
- choice = Choice()
-
- # link it with our Poll
- choice.poll = poll
-
- # give it some text
- choice.choice = "doin' fine..."
-
- # and let's say it's had some votes
- choice.votes = 3
-
- # save it
- choice.save()
-
- # try retrieving it from the database, using the poll object's reverse
- # lookup
- poll_choices = poll.choice_set.all()
- self.assertEquals(poll_choices.count(), 1)
-
- # finally, check its attributes have been saved
- choice_from_db = poll_choices[0]
- self.assertEquals(choice_from_db, choice)
- self.assertEquals(choice_from_db.choice, "doin' fine...")
- self.assertEquals(choice_from_db.votes, 3)
-
-
- def test_choice_defaults(self):
- choice = Choice()
- self.assertEquals(choice.votes, 0)
-
-
-
-class TestHomePageView(TestCase):
-
- def test_root_url_shows_links_to_all_polls(self):
- # set up some polls
- poll1 = Poll(question='6 times 7', pub_date=timezone.now())
- poll1.save()
- poll2 = Poll(question='life, the universe and everything', pub_date=timezone.now())
- poll2.save()
-
- response = self.client.get('/')
-
- template_names_used = [t.name for t in response.templates]
- self.assertIn('home.html', template_names_used)
-
- # check we've passed the polls to the template
- polls_in_context = response.context['polls']
- self.assertEquals(list(polls_in_context), [poll1, poll2])
-
- # check the poll names appear on the page
- self.assertIn(poll1.question, response.content)
- self.assertIn(poll2.question, response.content)
-
- # check the page also contains the urls to individual polls pages
- poll1_url = reverse('polls.views.poll', args=[poll1.id,])
- self.assertIn(poll1_url, response.content)
- poll2_url = reverse('polls.views.poll', args=[poll2.id,])
- self.assertIn(poll2_url, response.content)
-
-
-
-class TestSinglePollView(TestCase):
-
- def test_page_shows_poll_title_and_no_votes_message(self):
- # set up two polls, to check the right one is displayed
- poll1 = Poll(question='6 times 7', pub_date=timezone.now())
- poll1.save()
- poll2 = Poll(question='life, the universe and everything', pub_date=timezone.now())
- poll2.save()
-
- response = self.client.get('/poll/%d/' % (poll2.id, ))
-
- # check we've used the poll template
- self.assertTemplateUsed(response, 'poll.html')
-
- # check we've passed the right poll into the context
- self.assertEquals(response.context['poll'], poll2)
-
- # check the poll's question appears on the page
- self.assertIn(poll2.question, response.content)
-
- # check our 'no votes yet' message appears
- self.assertIn('No-one has voted on this poll yet', response.content)
-
-from polls.forms import PollVoteForm
-
-class TestPollsVoteForm(TestCase):
-
- def test_form_renders_poll_choices_as_radio_inputs(self):
- # set up a poll with a couple of choices
- poll1 = Poll(question='6 times 7', pub_date=timezone.now())
- poll1.save()
- choice1 = Choice(poll=poll1, choice='42', votes=0)
- choice1.save()
- choice2 = Choice(poll=poll1, choice='The Ultimate Answer', votes=0)
- choice2.save()
-
- # set up another poll to make sure we only see the right choices
- poll2 = Poll(question='time', pub_date=timezone.now())
- poll2.save()
- choice3 = Choice(poll=poll2, choice='PM', votes=0)
- choice3.save()
-
- # build a voting form for poll1
- form = PollVoteForm(poll=poll1)
-
- # check it has a single field called 'vote', which has right choices:
- self.assertEquals(form.fields.keys(), ['vote'])
-
- # choices are tuples in the format (choice_number, choice_text):
- self.assertEquals(form.fields['vote'].choices, [
- (choice1.id, choice1.choice),
- (choice2.id, choice2.choice),
- ])
-
- # check it uses radio inputs to render
- self.assertIn('input type="radio"', form.as_p())
-
-
- def test_page_shows_choices_using_form(self):
- # set up a poll with choices
- poll1 = Poll(question='time', pub_date=timezone.now())
- poll1.save()
- choice1 = Choice(poll=poll1, choice="PM", votes=0)
- choice1.save()
- choice2 = Choice(poll=poll1, choice="Gardener's", votes=0)
- choice2.save()
-
- response = self.client.get('/poll/%d/' % (poll1.id, ))
-
- # check we've passed in a form of the right type
- self.assertTrue(isinstance(response.context['form'], PollVoteForm))
-
- # and check the check the form is being used in the template,
- # by checking for the choice text
- self.assertIn(choice1.choice, response.content.replace('&#39;', "'"))
- self.assertIn(choice2.choice, response.content.replace('&#39;', "'"))
View
3  mysite/polls/tests/__init__.py
@@ -0,0 +1,3 @@
+from polls.tests.test_forms import *
+from polls.tests.test_models import *
+from polls.tests.test_views import *
View
58 mysite/polls/tests/test_forms.py
@@ -0,0 +1,58 @@
+from polls.forms import PollVoteForm
+from django.test import TestCase
+from django.utils import timezone
+from polls.models import Choice, Poll
+
+
+
+class TestPollsVoteForm(TestCase):
+
+ def test_form_renders_poll_choices_as_radio_inputs(self):
+ # set up a poll with a couple of choices
+ poll1 = Poll(question='6 times 7', pub_date=timezone.now())
+ poll1.save()
+ choice1 = Choice(poll=poll1, choice='42', votes=0)
+ choice1.save()
+ choice2 = Choice(poll=poll1, choice='The Ultimate Answer', votes=0)
+ choice2.save()
+
+ # set up another poll to make sure we only see the right choices
+ poll2 = Poll(question='time', pub_date=timezone.now())
+ poll2.save()
+ choice3 = Choice(poll=poll2, choice='PM', votes=0)
+ choice3.save()
+
+ # build a voting form for poll1
+ form = PollVoteForm(poll=poll1)
+
+ # check it has a single field called 'vote', which has right choices:
+ self.assertEquals(form.fields.keys(), ['vote'])
+
+ # choices are tuples in the format (choice_number, choice_text):
+ self.assertEquals(form.fields['vote'].choices, [
+ (choice1.id, choice1.choice),
+ (choice2.id, choice2.choice),
+ ])
+
+ # check it uses radio inputs to render
+ self.assertIn('input type="radio"', form.as_p())
+
+
+ def test_page_shows_choices_using_form(self):
+ # set up a poll with choices
+ poll1 = Poll(question='time', pub_date=timezone.now())
+ poll1.save()
+ choice1 = Choice(poll=poll1, choice="PM", votes=0)
+ choice1.save()
+ choice2 = Choice(poll=poll1, choice="Gardener's", votes=0)
+ choice2.save()
+
+ response = self.client.get('/poll/%d/' % (poll1.id, ))
+
+ # check we've passed in a form of the right type
+ self.assertTrue(isinstance(response.context['form'], PollVoteForm))
+
+ # and check the check the form is being used in the template,
+ # by checking for the choice text
+ self.assertIn(choice1.choice, response.content.replace('&#39;', "'"))
+ self.assertIn(choice2.choice, response.content.replace('&#39;', "'"))
View
116 mysite/polls/tests/test_models.py
@@ -0,0 +1,116 @@
+from django.core.urlresolvers import reverse
+from django.test import TestCase
+from django.utils import timezone
+from polls.models import Choice, Poll
+
+class TestPollsModel(TestCase):
+ def test_creating_a_new_poll_and_saving_it_to_the_database(self):
+ # start by creating a new Poll object with its "question" and
+ # "pub_date" attributes set
+ poll = Poll()
+ poll.question = "What's up?"
+ poll.pub_date = timezone.now()
+
+ # check we can save it to the database
+ poll.save()
+
+ # now check we can find it in the database again
+ all_polls_in_database = Poll.objects.all()
+ self.assertEquals(len(all_polls_in_database), 1)
+ only_poll_in_database = all_polls_in_database[0]
+ self.assertEquals(only_poll_in_database, poll)
+
+ # and check that it's saved its two attributes: question and pub_date
+ self.assertEquals(only_poll_in_database.question, "What's up?")
+ self.assertEquals(only_poll_in_database.pub_date, poll.pub_date)
+
+
+ def test_verbose_name_for_pub_date(self):
+ for field in Poll._meta.fields:
+ if field.name == 'pub_date':
+ self.assertEquals(field.verbose_name, 'Date published')
+
+
+ def test_poll_objects_are_named_after_their_question(self):
+ p = Poll()
+ p.question = 'How is babby formed?'
+ self.assertEquals(unicode(p), 'How is babby formed?')
+
+
+ def test_poll_can_tell_you_its_total_number_of_votes(self):
+ p = Poll(question='where',pub_date=timezone.now())
+ p.save()
+ c1 = Choice(poll=p,choice='here',votes=0)
+ c1.save()
+ c2 = Choice(poll=p,choice='there',votes=0)
+ c2.save()
+
+ self.assertEquals(p.total_votes(), 0)
+
+ c1.votes = 1000
+ c1.save()
+ c2.votes = 22
+ c2.save()
+ self.assertEquals(p.total_votes(), 1022)
+
+
+
+class TestPollChoicesModel(TestCase):
+
+ def test_creating_some_choices_for_a_poll(self):
+ # start by creating a new Poll object
+ poll = Poll()
+ poll.question="What's up?"
+ poll.pub_date = timezone.now()
+ poll.save()
+
+ # now create a Choice object
+ choice = Choice()
+
+ # link it with our Poll
+ choice.poll = poll
+
+ # give it some text
+ choice.choice = "doin' fine..."
+
+ # and let's say it's had some votes
+ choice.votes = 3
+
+ # save it
+ choice.save()
+
+ # try retrieving it from the database, using the poll object's reverse
+ # lookup
+ poll_choices = poll.choice_set.all()
+ self.assertEquals(poll_choices.count(), 1)
+
+ # finally, check its attributes have been saved
+ choice_from_db = poll_choices[0]
+ self.assertEquals(choice_from_db, choice)
+ self.assertEquals(choice_from_db.choice, "doin' fine...")
+ self.assertEquals(choice_from_db.votes, 3)
+
+
+ def test_choice_defaults(self):
+ choice = Choice()
+ self.assertEquals(choice.votes, 0)
+
+
+ def test_choice_can_calculate_its_own_percentage_of_votes(self):
+ poll = Poll(question='who?', pub_date=timezone.now())
+ poll.save()
+ choice1 = Choice(poll=poll,choice='me',votes=2)
+ choice1.save()
+ choice2 = Choice(poll=poll,choice='you',votes=1)
+ choice2.save()
+
+ self.assertEquals(choice1.percentage(), 100 * 2 / 3.0)
+ self.assertEquals(choice2.percentage(), 100 * 1 / 3.0)
+
+ # also check 0-votes case
+ choice1.votes = 0
+ choice1.save()
+ choice2.votes = 0
+ choice2.save()
+ self.assertEquals(choice1.percentage(), 0)
+ self.assertEquals(choice2.percentage(), 0)
View
123 mysite/polls/tests/test_views.py
@@ -0,0 +1,123 @@
+from django.core.urlresolvers import reverse
+from django.test import TestCase
+from django.utils import timezone
+from polls.models import Choice, Poll
+
+
+class TestHomePageView(TestCase):
+
+ def test_root_url_shows_links_to_all_polls(self):
+ # set up some polls
+ poll1 = Poll(question='6 times 7', pub_date=timezone.now())
+ poll1.save()
+ poll2 = Poll(question='life, the universe and everything', pub_date=timezone.now())
+ poll2.save()
+
+ response = self.client.get('/')
+
+ template_names_used = [t.name for t in response.templates]
+ self.assertIn('home.html', template_names_used)
+
+ # check we've passed the polls to the template
+ polls_in_context = response.context['polls']
+ self.assertEquals(list(polls_in_context), [poll1, poll2])
+
+ # check the poll names appear on the page
+ self.assertIn(poll1.question, response.content)
+ self.assertIn(poll2.question, response.content)
+
+ # check the page also contains the urls to individual polls pages
+ poll1_url = reverse('polls.views.poll', args=[poll1.id,])
+ self.assertIn(poll1_url, response.content)
+ poll2_url = reverse('polls.views.poll', args=[poll2.id,])
+ self.assertIn(poll2_url, response.content)
+
+
+
+class TestSinglePollView(TestCase):
+
+ def test_page_shows_poll_title_and_no_votes_message(self):
+ # set up two polls, to check the right one is displayed
+ poll1 = Poll(question='6 times 7', pub_date=timezone.now())
+ poll1.save()
+ poll2 = Poll(question='life, the universe and everything', pub_date=timezone.now())
+ poll2.save()
+
+ response = self.client.get('/poll/%d/' % (poll2.id, ))
+
+ # check we've used the poll template
+ self.assertTemplateUsed(response, 'poll.html')
+
+ # check we've passed the right poll into the context
+ self.assertEquals(response.context['poll'], poll2)
+
+ # check the poll's question appears on the page
+ self.assertIn(poll2.question, response.content)
+
+ # check our 'no votes yet' message appears
+ self.assertIn('No-one has voted on this poll yet', response.content)
+
+
+ def test_view_shows_percentage_of_votes(self):
+ # set up a poll with choices
+ poll1 = Poll(question='6 times 7', pub_date=timezone.now())
+ poll1.save()
+ choice1 = Choice(poll=poll1, choice='42', votes=1)
+ choice1.save()
+ choice2 = Choice(poll=poll1, choice='The Ultimate Answer', votes=2)
+ choice2.save()
+
+ response = self.client.get('/poll/%d/' % (poll1.id, ))
+
+ # check the percentages of votes are shown, sensibly rounded
+ self.assertIn('33 %: 42', response.content)
+ self.assertIn('67 %: The Ultimate Answer', response.content)
+
+ # and that the 'no-one has voted' message is gone
+ self.assertNotIn('No-one has voted', response.content)
+
+ def test_view_shows_total_votes(self):
+ # set up a poll with choices
+ poll1 = Poll(question='6 times 7', pub_date=timezone.now())
+ poll1.save()
+ choice1 = Choice(poll=poll1, choice='42', votes=1)
+ choice1.save()
+ choice2 = Choice(poll=poll1, choice='The Ultimate Answer', votes=2)
+ choice2.save()
+
+ response = self.client.get('/poll/%d/' % (poll1.id, ))
+ self.assertIn('3 votes', response.content)
+
+ # also check we only pluralise "votes" if necessary. details!
+ choice2.votes = 0
+ choice2.save()
+ response = self.client.get('/poll/%d/' % (poll1.id, ))
+ self.assertIn('1 vote', response.content)
+ self.assertNotIn('1 votes', response.content)
+
+
+ def test_view_can_handle_votes_via_POST(self):
+ # set up a poll with choices
+ poll1 = Poll(question='6 times 7', pub_date=timezone.now())
+ poll1.save()
+ choice1 = Choice(poll=poll1, choice='42', votes=1)
+ choice1.save()
+ choice2 = Choice(poll=poll1, choice='The Ultimate Answer', votes=3)
+ choice2.save()
+
+ # set up our POST data - keys and values are strings
+ post_data = {'vote': str(choice2.id)}
+
+ # make our request to the view
+ poll_url = '/poll/%d/' % (poll1.id,)
+ response = self.client.post(poll_url, data=post_data)
+
+ # retrieve the updated choice from the database
+ choice_in_db = Choice.objects.get(pk=choice2.id)
+
+ # check it's votes have gone up by 1
+ self.assertEquals(choice_in_db.votes, 4)
+
+ # always redirect after a POST - even if, in this case, we go back
+ # to the same page.
+ self.assertRedirects(response, poll_url)
View
9 mysite/polls/views.py
@@ -1,7 +1,9 @@
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect
from django.shortcuts import render
from polls.forms import PollVoteForm
-from polls.models import Poll
+from polls.models import Choice, Poll
def home(request):
context = {'polls': Poll.objects.all()}
@@ -9,6 +11,11 @@ def home(request):
def poll(request, poll_id):
+ if request.method == 'POST':
+ choice = Choice.objects.get(id=request.POST['vote'])
+ choice.votes += 1
+ choice.save()
+ return HttpResponseRedirect(reverse('polls.views.poll', args=[poll_id,]))
poll = Poll.objects.get(pk=poll_id)
form = PollVoteForm(poll=poll)
return render(request, 'poll.html', {'poll': poll, 'form': form})
View
4 tutorial03.rst
@@ -376,7 +376,7 @@ But the point of TDD is to be driven by the tests. At each stage, we only write
So, rather than anticipate what we might want to put in our HttpResponse, let's go to the FT now to see what to do next.::
- python functional_tests.py
+ python manage.py test fts
======================================================================
ERROR: test_voting_on_a_new_poll (tests.TestPolls)
----------------------------------------------------------------------
@@ -602,7 +602,7 @@ Ta-da!::
What do the FTs say now?::
- python functional_tests.py
+ python manage.py test fts
======================================================================
ERROR: test_voting_on_a_new_poll (tests.TestPolls)
----------------------------------------------------------------------
View
4 tutorial04.rst
@@ -428,9 +428,9 @@ And that should get the tests passing! If you're curious to see what the form H
Right, where where we? Let's do a quick check of the functional tests.
-(*incidentally, are you rather bored of watching the FT run through the admin test each time? I was, so I've built in a second argument to the FT runner that lets you filter by name of test - just pass in* ``polls`` *and it will only run FTs in files whose names contain the world* ``polls``.)::
+(*incidentally, are you rather bored of watching the FT run through the admin test each time? If so, you can temporarily disable it by renaming its test method from* ``test_can_create_new_poll_via_admin_site`` *to* ``DONTtest_can_create_new_poll_via_admin_site`` *that's called "Dontifying"... remember to change it back before the end though!*)
- python functional_tests.py polls
+ python manage.py test fts
[...]
AssertionError: 0 != 3
View
127 tutorial05.rst
@@ -17,10 +17,10 @@ Here's the outline of what we're going to do in this tutorial:
Finishing the FT
----------------
-Let's pick up from the ``TODO`` in our FT, and extend it to include viewing the effects of submitting a vote on a poll. In ``fts/test_polls.py``:
+Let's pick up from the ``TODO`` in our FT, and extend it to include viewing the effects of submitting a vote on a poll. In ``fts/tests.py``:
.. sourcecode:: python
- :filename: mysite/fts/test_polls.py
+ :filename: mysite/fts/tests.py
[...]
@@ -38,17 +38,43 @@ Let's pick up from the ``TODO`` in our FT, and extend it to include viewing the
# The page also says "1 vote"
self.assertIn('1 vote', body_text)
- self.fail('TODO')
+ # But not "1 votes" -- Herbert is impressed at the attention to detail
+ self.assertNotIn('1 votes', body_text)
-We'll leave the "TODO" in, because we'll want to exercise the site a litte further, to make sure other votes update the counters appropriately. But let's see if we can get a single vote working for now.
+ # Herbert suspects that the website isn't very well protected
+ # against people submitting multiple votes yet, so he tries
+ # to do a little astroturfing
+ self.browser.find_element_by_css_selector("input[value='1']").click()
+ self.browser.find_element_by_css_selector("input[type='submit']").click()
+
+ # The page refreshes, and he sees that his choice has updated the
+ # results. it still says # "100 %: very awesome".
+ body_text = self.browser.find_element_by_tag_name('body').text
+ self.assertIn('100 %: Very awesome', body_text)
+
+ # But the page now says "2 votes"
+ self.assertIn('2 votes', body_text)
+
+ # Cackling manically over his l33t haxx0ring skills, he tries
+ # voting for a different choice
+ self.browser.find_element_by_css_selector("input[value='2']").click()
+ self.browser.find_element_by_css_selector("input[type='submit']").click()
+
+ # Now, the percentages update, as well as the votes
+ body_text = self.browser.find_element_by_tag_name('body').text
+ self.assertIn('67 %: Very awesome', body_text)
+ self.assertIn('33 %: Quite awesome', body_text)
+ self.assertIn('3 votes', body_text)
+
+ # Satisfied, he goes back to sleep
If you run the FTs, you should see something like this::
======================================================================
- FAIL: test_voting_on_a_new_poll (test_polls.TestPolls)
+ FAIL: test_voting_on_a_new_poll (tests.TestPolls)
----------------------------------------------------------------------
Traceback (most recent call last):
- File "/home/harry/workspace/tddjango_site/source/mysite/fts/test_polls.py", line 126, in test_voting_on_a_new_poll
+ File "/home/harry/workspace/tddjango_site/source/mysite/fts/tests.py", line 126, in test_voting_on_a_new_poll
self.assertIn('100 %: Very awesome', body_text)
AssertionError: '100 %: Very awesome' not found in u'Poll Results\nHow awesome is Test-Driven Development?\nNo-one has voted on this poll yet\nAdd your vote\nVote:\nVery awesome\nQuite awesome\nModerately awesome'
@@ -219,9 +245,9 @@ Re-running the tests should give us 9 tests again, and we end up with 3 much mor
At this stage your polls app should look something like this::
`-- polls
+ |-- __init__.py
|-- admin.py
|-- forms.py
- |-- __init__.py
|-- models.py
|-- templates
| |-- home.html
@@ -265,9 +291,8 @@ The Django Test Client can generate POST requests as easily as GET ones, we just
post_data = {'vote': str(choice2.id)}
# make our request to the view
- client = Client()
poll_url = '/poll/%d/' % (poll1.id,)
- response = client.post(poll_url, data=post_data)
+ response = self.client.post(poll_url, data=post_data)
# retrieve the updated choice from the database
choice_in_db = Choice.objects.get(pk=choice2.id)
@@ -401,13 +426,13 @@ Lovely! let's see that at work::
Creating test database for alias 'default'...
.........
----------------------------------------------------------------------
- Ran 9 tests in 0.023s
+ Ran 10 tests in 0.023s
OK
Hooray! Let's see if it gets the FT any further::
- $ python functional_tests.py polls
+ $ python manage.py test fts
[...]
AssertionError: '100 %: Very awesome' not found in u'Poll Results\nHow awesome is Test-Driven Development?\nNo-one has voted on this poll yet\nAdd your vote\nVote:\nVery awesome\nQuite awesome\nModerately awesome'
@@ -427,8 +452,7 @@ a quick test in ``test_views``:
choice2 = Choice(poll=poll1, choice='The Ultimate Answer', votes=2)
choice2.save()
- client = Client()
- response = client.get('/poll/%d/' % (poll1.id, ))
+ response = self.client.get('/poll/%d/' % (poll1.id, ))
# check the percentages of votes are shown, sensibly rounded
self.assertIn('33 %: 42', response.content)
@@ -629,7 +653,7 @@ Let's hope this test/code cycle is self-explanatory. Start with ``test_models.py
[...]
def test_poll_can_tell_you_its_total_number_of_votes(self):
- p = Poll(question='where',pub_date=tiemzone.now())
+ p = Poll(question='where',pub_date=timezone.now())
p.save()
c1 = Choice(poll=p,choice='here',votes=0)
c1.save()
@@ -742,10 +766,10 @@ And re-run the tests::
At last! What about the FT?::
======================================================================
- FAIL: test_voting_on_a_new_poll (test_polls.TestPolls)
+ FAIL: test_voting_on_a_new_poll (tests.TestPolls)
----------------------------------------------------------------------
Traceback (most recent call last):
- File "/home/harry/workspace/tddjango_site/source/mysite/fts/test_polls.py", line 126, in test_voting_on_a_new_poll
+ File "/home/harry/workspace/tddjango_site/source/mysite/fts/tests.py", line 126, in test_voting_on_a_new_poll
self.assertIn('100 %: Very awesome', body_text)
AssertionError: '100 %: Very awesome' not found in u'Poll Results\nHow awesome is Test-Driven Development?\n0 %: Very awesome\n0 %: Quite awesome\n0 %: Moderately awesome\nNo-one has voted on this poll yet\nAdd your vote\nVote:\nVery awesome\nQuite awesome\nModerately awesome'
@@ -786,7 +810,7 @@ Hmm, not quite. What is missing? The "submit" button doesn't seem to be workin
Re-running the FT, we get::
- AssertionError: '100 %: Very awesome' not found in u"Forbidden (403)\nCSRF verification failed. Request aborted.\nHelp\nReason given for failure:\n CSRF token missing or incorrect.\n \nIn general, this can occur when there is a genuine Cross Site Request Forgery, or when Django's CSRF mechanism has not been used correctly. For POST forms, you need to ensure:\nThe view function uses RequestContext for the template, instead of Context.\nIn the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.\nIf you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.\nYou're seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.\nYou can customize this page using the CSRF_FAILURE_VIEW setting."
+ AssertionError: '100 %: Very awesome' not found in u'Forbidden (403)\nCSRF verification failed. Request aborted.\nMore information is available with DEBUG=True.'
Pretty helpful, as error messages go. Let's add an amazing Django voodoo CSRF tag:
@@ -820,7 +844,7 @@ https://docs.djangoproject.com/en/1.4/ref/templates/builtins/
Now what?::
- FAIL: test_voting_on_a_new_poll (test_polls.TestPolls)
+ FAIL: test_voting_on_a_new_poll (tests.TestPolls)
AssertionError: '1 vote' not found in u'Poll Results\nHow awesome is Test-Driven Development?\n100 %: Very awesome\n0 %: Quite awesome\n0 %: Moderately awesome\nAdd your vote\nVote:\nVery awesome\nQuite awesome\nModerately awesome'
Aha, looks like that ``total_votes`` function is going to come in useful again!
@@ -839,14 +863,13 @@ Let's add a tiny test to our ``test_views.py``:
choice2 = Choice(poll=poll1, choice='The Ultimate Answer', votes=2)
choice2.save()
- client = Client()
- response = client.get('/poll/%d/' % (poll1.id, ))
+ response = self.client.get('/poll/%d/' % (poll1.id, ))
self.assertIn('3 votes', response.content)
# also check we only pluralise "votes" if necessary. details!
choice2.votes = 0
choice2.save()
- response = client.get('/poll/%d/' % (poll1.id, ))
+ response = self.client.get('/poll/%d/' % (poll1.id, ))
self.assertIn('1 vote', response.content)
self.assertNotIn('1 votes', response.content)
@@ -916,69 +939,15 @@ Unit tests snow pass::
Now, how about those functional tests?::
- $ python functional_tests.py polls
+ $ python manage.py test fts
AssertionError: TODO
-That looks good. Let's extend the FT to make sure that multiple votes add up the way we want them to:
-
-.. sourcecode:: python
- :filename: mysite/fts/test_polls.py
+That looks good. How about our fts?::
- [...]
- # The page also says "1 vote"
- self.assertIn('1 vote', body_text)
-
- # Harold suspects that the website isn't very well protected
- # against people submitting multiple votes yet, so he tries
- # to do a little astroturfing
- self.browser.find_element_by_css_selector("input[value='1']").click()
- self.browser.find_element_by_css_selector("input[type='submit']").click()
-
- # The page refreshes, and he sees that his choice has updated the
- # results. it still says # "100 %: very awesome".
- body_text = self.browser.find_element_by_tag_name('body').text
- self.assertIn('100 %: Very awesome', body_text)
-
- # But the page now says "2 votes"
- self.assertIn('2 votes', body_text)
-
- # Cackling manically over his l33t haxx0ring skills, he tries
- # voting for a different choice
- self.browser.find_element_by_css_selector("input[value='2']").click()
- self.browser.find_element_by_css_selector("input[type='submit']").click()
-
- # Now, the percentages update, as well as the votes
- body_text = self.browser.find_element_by_tag_name('body').text
- self.assertIn('67 %: Very awesome', body_text)
- self.assertIn('33 %: Quite awesome', body_text)
- self.assertIn('3 votes', body_text)
-
- # Satisfied, he goes back to sleep
-
-
-Let's try that::
-
- $ python functional_tests.py polls
- [...]
- Ran 1 test in 5.674s
- OK
-
-
-
-Hooray! Just to be safe, it's worth running **all** the unit tests, and all
-the functional tests too...::
-
- $ python manage.py test
- [...]
- Ran 335 tests in 1.908s
- OK
-
-
- $ python functional_tests.py
- [...]
- Ran 2 tests in 10.580s
+ $ python manage.py test fts
+ Ran 2 tests in 9.606s
OK
Please sign in to comment.
Something went wrong with that request. Please try again.