Reputation: 63
I am new to programming and Django in general. I am trying to test one of my functions to make sure that a validation error is raised. The test confirms that the error is raised but also says the test Failed. How is this possible?
**models.py**
def check_user_words(sender, instance, **kwargs):
for field in instance._meta.get_fields():
#field_name = getattr(instance, field.attname)
if (isinstance(field, models.CharField) and
contains_bad_words(getattr(instance, field.attname))):
raise ValidationError("We don't use words like '{}' around here!".format(getattr(instance, field.attname)))
#tests.py
from __future__ import unicode_literals
import datetime
from django.test import TestCase
from django.utils import timezone
from django.test import TestCase
from django.urls import reverse
from .models import Question, Choice, contains_bad_words, check_user_words
from django.core.exceptions import ValidationError
def create_question(question_text, days):
time = timezone.now() + datetime.timedelta(days=days)
return Question.objects.create(question_text=question_text, pub_date=time)
class ContainsBadWordsTests(TestCase):
def test_check_user_words(self):
question = create_question(question_text="What a minute bucko", days=1)
with self.assertRaises(ValidationError):
check_user_words(question)
question.full_clean()
#after running python manage.py test polls
......
raise ValidationError("We don't use words like '{}' around here!".format(getattr(instance, field.attname)))
ValidationError: [u"We don't use words like 'What a minute bucko' around here!"]
from __future__ import unicode_literals .... (and others)
filepath = "polls/static/polls/blacklist.yaml"
config = yaml_loader(filepath)
blacklist = [word.lower() for word in config['blacklist']]
def contains_bad_words(user_input_txt):
""" remove punctuation from text
and make it case-insensitive"""
user_typ = user_input_txt.encode()
translate_table = maketrans(string.punctuation, 32 * " ")
words = user_typ.translate(translate_table).lower().split()
for bad_word in blacklist:
for word in words:
if word == bad_word:
return True
return False
@receiver(pre_save)
def check_user_words(sender, instance, **kwargs):
for field in instance._meta.get_fields():
if (isinstance(field, models.CharField) and
contains_bad_words(getattr(instance, field.attname))):
raise ValidationError("We don't use words like '{}' around here!".format(getattr(instance, field.attname)))
Upvotes: 1
Views: 3445
Reputation: 31504
We need to see more of your code (specifically create_question()
and how check_user_words
is connected to a signal) to be sure, but I think the issue is that you are using a post_save
signal handler to execute check_user_words()
.
If this is the case, then the reason your test is failing is that create_question()
will cause the post_save
signal to fire, and check_user_words()
will be executed immediately - i.e., before the with self.assertRaises
context, and hence your test fails.
If this is the case, then try this:
def test_check_user_words(self):
with self.assertRaises(ValidationError):
create_question(question_text="What a minute bucko", days=1)
This test should now pass, because the validation error will be thrown as soon as you try to create the question.
Note however that doing this in a signal will result in an uncaught exception when something tries to save an object. Depending on what your use case is, you might be better off doing this in the clean()
method of the model itself (see docs here), because this will cause appropriate errors to be reported on model forms etc:
def clean(self):
for field in instance._meta.get_fields():
if (isinstance(field, models.CharField) and contains_bad_words(getattr(instance, field.attname))):
raise ValidationError("We don't use words like '{}' around here!".format(getattr(instance, field.attname)))
(and then drop your signal handler). Then you can test this with:
q = create_question(question_text="What a minute bucko", days=1)
with self.assertRaises(ValidationError):
q.clean()
Upvotes: 1