Search code examples
pythondjangotestingdjango-testing

How to test a view in Django


I am learning basics of Django following official tutorial and adding some new features to my application. So I added a view that can nullify all votes of particular question

def nullask(request, question_id):
    question=get_object_or_404(Question, pk = question_id)
    if request.method == "GET":
        return render(request, "polls/nullifyask.html", {"question":question})
    else:
        for i in question.choice_set.all():
            i.votes = 0
            i.save()
        return HttpResponseRedirect(reverse("polls:index"))

So it works fine, but I wanted to practice in writing tests and wanted to write a test that test that votes are really nullified. Here it is

class NullingViewTest(TestCase):
    def test_nulling(self):
        q=create_question(text="Future", days=-1)
        choice=q.choice_set.create(choice_text="1", votes=10)
        response=self.client.post(reverse('polls:nullask', args=(q.id,)))
        self.assertEqual(choice.votes, 0)

It does't work(votes are not changing from 10 to 0 and AssertionError: 10 != 0 appears. I understand why this happens but cannot make it work like I want. What should I do here is nullify ask.html:

<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <title>Nullyfying of {{question_id}}</title>
  </head>
  <body>
    <form method="post">
      {% csrf_token %}
        <legend><h1>Do you want to nullify votes of question {{question.text}}</h1></legend>
        <input type="submit" name="Yes" value="Yes!">
    </form>
  </body>
</html>

Here are models:

class Question(models.Model):
    text=models.CharField(max_length=200)
    productiondate=models.DateTimeField("date published")

    def was_published_recently(self):
        return self.productiondate >= timezone.now() - datetime.timedelta(days=1) and self.productiondate<timezone.now()
    def __str__(self):
        return self.text
    class Meta:
        permissions=(("can_nullify","User can nullify votes" ),)
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    def votesx2(self):
        return self.votes*2
    def __str__(self):
        return self.choice_text

I have tried using question=response.context['question'] in the test but it resulted in an error


Solution

  • In your test, you need to update the choice Python object to reflect the updated vote count in the database. This can be done by calling refresh_from_db() on the object in your test after you POST to nullask.

    class NullingViewTest(TestCase):
        def test_nulling(self):
            q=create_question(text="Future", days=-1)
            choice=q.choice_set.create(choice_text="1", votes=10)
            response=self.client.post(reverse('polls:nullask', args=(q.id,)))
            
            # Update the Python object to reflect changes in the database
            choice.refresh_from_db()
            
            self.assertEqual(choice.votes, 0)