Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Creando tests para DetailView

8/17
Recursos

Aportes 15

Preguntas 7

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Buenas, les muestro cómo logré hacer la validación de crear preguntas sin respuestas:

Archivo: models

class Question(models.Model):
    # id el campo ID no es necesario porque Django la crea por nosotros.
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField("date published")

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

        if self.choice_set.all().count() == 0:
            super().delete()
            raise Exception("Question must have at least one choice")

Archivo: tests

    def test_question_wo_answers(self):
        question = Question(question_text="test", pub_date=timezone.now())
        with self.assertRaises(Exception):
            question.save()

Para el segundo reto, hice unos cambios en el archivo admin para que al guardar una pregunta pida de una vez las respuestas, y no deje guardar sin respuestas.

from django.contrib import admin
from .models import Question,Choice
from django import forms
from django.forms.models import BaseInlineFormSet

class AtLeastOneRequiredInlineFormSet(BaseInlineFormSet):

    def clean(self):
        """Check that at least one choice has been entered."""
        super(AtLeastOneRequiredInlineFormSet, self).clean()
        if any(self.errors):
            return
        if not any(cleaned_data and not cleaned_data.get('DELETE', False)
            for cleaned_data in self.cleaned_data):
            raise forms.ValidationError('At least one choice required.')


class ChoicesInline(admin.TabularInline):
    model = Choice
    formset= AtLeastOneRequiredInlineFormSet
    extra = 1
    exclude= ['votes']

class QuestionAdmin(admin.ModelAdmin):
    inlines=(ChoicesInline,)

    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for obj in formset.deleted_objects:
            obj.delete()        
        for instance in instances:
            instance.save()            


admin.site.register(Question, QuestionAdmin)
#admin.site.register(Choice)

Primer reto

class QuestionResultsViewTests(TestCase):
    def test_question_not_exists(self):
        """ If question id not exists, get 404 """
        response= self.client.get(reverse("polls:results", kwargs={'pk':1}))
        self.assertEqual(response.status_code, 404)

    def test_future_question(self):
        """ If it is a question from future, get 404 """
        future_question= create_question("Future question", 30)
        response= self.client.get(reverse("polls:results", kwargs={'pk':future_question.pk}))
        self.assertEqual(response.status_code, 404)

    def test_past_question(self):
        """ If it is a question from past, may display it """
        past_question= create_question("Past question", -10)
        response= self.client.get(reverse("polls:results", kwargs={'pk':past_question.pk}))
        self.assertContains(response, past_question.question_text)
    
    def test_display_question_choices_and_votes(self):
        """Page may display votes for every choice of a question"""
        question= create_question("Question", -1)
        choice1= Choice.objects.create(choice_text="Choice 1", question= question, votes=1)
        choice2= Choice.objects.create(choice_text="Choice 2", question= question)
        choice3= Choice.objects.create(choice_text="Choice 3", question= question)
        choice4= Choice.objects.create(choice_text="Choice 4", question= question)
        choice5= Choice.objects.create(choice_text="Choice 5", question= question)

        response = self.client.get(reverse("polls:results", kwargs={'pk': question.pk}))

        self.assertContains(response, question.question_text)
        self.assertContains(response, choice1.choice_text + ' -- ' + '1 vote' )
        self.assertContains(response, choice2.choice_text + ' -- ' + '0 votes' )
        self.assertContains(response, choice3.choice_text + ' -- ' + '0 votes' )
        self.assertContains(response, choice4.choice_text + ' -- ' + '0 votes' )
        self.assertContains(response, choice5.choice_text + ' -- ' + '0 votes' )

Flujo (tests):

  1. Identificar el problema

  2. Crear test un para solucionar ese problema

  3. Ejecutamos el test para ver que funciona

  4. Arreglamos el problema

  5. Volvemos a ejecutar los tests

Hola, aquí dejo la solución de evitar crear Question sin choices:

Test

  def test_question_without_choices(self):
    """
    Question without choices
    """
    question = Question(question_text="Question without choices", pub_date=timezone.now())
    with self.assertRaises(ValueError):
      question.save()

Models:

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text

    def was_published_recently(self):
        return timezone.now() > self.pub_date >= timezone.now() - datetime.timedelta(days=1)

    def save(self, *args, **kwargs):
        choices = kwargs.get('choices')
        if choices and len(choices) > 0:
            kwargs.pop('choices', None)
            super().save(*args, **kwargs)     
            for choice in choices:
                choice.question = self
                choice.save()
        else:
            raise ValueError("Should have choices")

Funcion create_question con el cambio:

def create_question(question_text, days):
  """
  Create a question with the given 'question_text' and 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).
  """
  time = timezone.now() + datetime.timedelta(days=days)
  question = Question(question_text=question_text, pub_date=time)
  choice1 = Choice(question=question, choice_text="Choice 1", votes=0)
  choice2 = Choice(question=question, choice_text="Choice 2", votes=0)
  question.save(choices=(choice1, choice2))
  return question

comparto mi solución del reto para mostrar preguntas que tengan mas de dos respuestas

    def get_queryset(self) :
        """Retorna las ultimas cinco publiaciones """

        question = Question.objects.filter(pub_date__lte = timezone.now())
        question = question.alias(entries=Count('choice')).filter(entries__gt=2)
        return question.order_by("-pub_date")[:5]

Tests de ResultView

class QuestionResultViewTests(TestCase):
    def test_no_question(self):
        """
        The result view of a question that doesn't exist
        return a 404 error not found
        """
        url = reverse("polls:results", args=(1,))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_future_question(self):
        """
        The result view of a question with a pub_date in the future
        return a 404 error not found
        """
        future_question = create_question(question_text="¿Quien es el mejor CD de Platzi?", days=5)
        url = reverse("polls:results", args=(future_question.id,))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_past_question(self):
        """
        The result view of a question with a pub_date in the past
        displays the question's text
        """
        past_question = create_question(question_text="Past question", days=-30)
        url = reverse("polls:results", args=(past_question.id,))
        response = self.client.get(url)
        self.assertContains(response, past_question.question_text)

En cuanto al segundo test, asegurarse de no crear una question sino tiene choices, este fue el test que implemente en QuestionModelTests:

class QuestionModelTests(TestCase):
	...

    def test_create_question_without_choices(self):
        """If the questions hasn't choices it is deleted."""

        question = Question.objects.create(
            question_text="¿Quien es el mejor CD de Platzi?",
            pub_date=timezone.now(),
            choices=0)

        if question.choices <= 1:
            question.delete()

        questions_count = len(Question.objects.all())

        self.assertEqual(questions_count, 0)

Así es como quedo mi modelo Question:

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField("date published")
    choices = models.IntegerField(default=0)

    def __str__(self):
        return self.question_text

    def was_published_recently(self):
        return timezone.now() >= self.pub_date >= timezone.now() - datetime.timedelta(days=1)

realmente no estamos creando preguntas nunca, tengo entendido, lo que podemos hacer es ocultar las preguntas sin opciones.

Para el 2do reto, modifiqué la funcion save() que trae el modelo Question por defecto, para que después de guardar la instancia, cree automáticamente un Choice con el texto “Ninguno”. De esa forma, siempre que se guarde una Question, automáticamente se creará una Choice para esa Question

Si alguno le quedo sonando lo del coverage:
Toca instalar:

pip install coverage
coverage run manage.py test polls -v 2 && coverage report

Y para evitar que revise todos los archivos se crea un archivo .coveragerc en la raíz del proyecto

[run]
omit= */tests.py, */migrations/*, */urls.py, */settings.py, */wsgi.py, */manage.py, fabfile.py, */__init__.py

[report]
show_missing = true

Bueno la verdad he quedado bloqueado aqui, si alguien me puede ayudar

Test para la vista ResultsView()

class QuestionResultsViewTests(TestCase):
    """Test module for the Results view."""

    def test_no_question(self):
        """
        The results view without a question returns 
        a 404 not found.
        """
        response = self.client.get(reverse('polls:results', args=(1,)))
        self.assertEqual(response.status_code, 404)      

    def test_future_question(self):
        """
        The results view of a question with a pub_date in the future
        returns a 404 not found.
        """
        future_question = create_question("Future question.", days=30)
        url = reverse('polls:results', args=(future_question.id,))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_past_question(self):
        """
        The results view of a question with a pub_date in the past
        displays the question's text.
        """
        past_question = create_question("Past question.", days=-30)
        url = reverse('polls:results', args=(past_question.id,))
        response = self.client.get(url)
        self.assertContains(response, past_question.question_text)

Aquí va mi reto para el video 7:

Test para la vista results

class ResultsViewTest(TestCase):
    def test_one_singular_vote(self):
        '''
        When Choice.votes is 1 the view shouls display "question text -- 1 vote"
        '''
        question = Question(question_text='Question #67676')
        question.save()
        choice = Choice(choice_text='choice #09821398', votes=1, question_id=question)
        choice.save()
        response = self.client.get(reverse('polls:results', args=(question.pk,)))
        self.assertNotContains(response, f'{choice.choice_text} -- {choice.votes} votes')
        self.assertContains(response, f'{choice.choice_text} -- {choice.votes} vote')


    def test_pluralize_multiple_votes(self):
        '''
        when choice.votes is greather than 1 view should display "quesion text -- n votes"
        '''
        question = Question(question_text='Question #032439')
        question.save()
        choice = Choice(choice_text='choice #383', votes=3, question_id=question)
        choice.save()
        response = self.client.get(reverse('polls:results', args=(question.pk,)))
        self.assertContains(response, f'{choice.choice_text} -- {choice.votes} votes')

Si tienen feedback es bien recibido 😃

Alt + Shift Para cambiarle el lenguaje al teclado profe @Facundo