I have several functional views that are passed parameters. I'm trying to write tests to check the url, status, etc. So far I'm starting with just testing the URL name. I have some querysets in the view and it looks from the traceback that I need to define objects for the queries in my setUp
urls
app_name = 'gradebook'
urlpatterns = [
path('updatesinglegrade/<int:assess_pk>/<int:class_pk>/<int:grade_pk>/',
views.updatesinglegrade, name='updatesinglegrade'),
]
view
def updatesinglegrade(request, assess_pk, class_pk, grade_pk):
grade = Grade.objects.get(id=grade_pk)
gradescale = GradeBookSetup.objects.get(user=request.user)
scale_choice = grade_scale_choice(gradescale.scale_mode)
form = UpdateGradeForm(gradescale=scale_choice)
context = {'form': form}
context['grade'] = grade
if request.method == 'POST':
form = UpdateGradeForm(
request.POST, gradescale=scale_choice)
if form.is_valid():
cd = form.cleaned_data
grade.score = cd['score']
grade.save()
return redirect('gradebook:assessdetail', assess_pk, class_pk)
else:
return render(request, "gradebook/grade_single_form.html", context)
else:
return render(request, "gradebook/grade_single_form.html", context)
test
class UpdateSingleGradeTests(TestCase):
def setUp(self):
self.user = CustomUser.objects.create_user(
username='tester',
email='[email protected]',
password='tester123'
)
login = self.client.login(username='tester', password='tester123')
def test_updatesinglegrade_url_name(self):
response = self.client.get(reverse('gradebook:updatesinglegrade', kwargs={
'assess_pk': 1, 'class_pk': 2, 'grade_pk': 3}))
self.assertEqual(response.status_code, 200)
traceback
======================================================================
ERROR: test_updatesinglegrade_url_name (gradebook.tests.UpdateSingleGradeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\Doug\OneDrive\django\gradebook\gradebook\tests.py", line 102, in test_updatesinglegrade_url_name
response = self.client.get(reverse('gradebook:updatesinglegrade', kwargs={
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 742, in get
response = super().get(path, data=data, secure=secure, **extra)
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 396, in get
return self.generic('GET', path, secure=secure, **{
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 473, in generic
return self.request(**r)
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 719, in request
self.check_exception(response)
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\client.py", line 580, in check_exception
raise exc_value
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
response = get_response(request)
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\contrib\auth\decorators.py", line 21, in _wrapped_view
return view_func(request, *args, **kwargs)
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\contrib\auth\decorators.py", line 21, in _wrapped_view
return view_func(request, *args, **kwargs)
File "C:\Users\Doug\OneDrive\django\gradebook\gradebook\views.py", line 1030, in updatesinglegrade
grade = Grade.objects.get(id=grade_pk)
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\query.py", line 435, in get
raise self.model.DoesNotExist(
gradebook.models.Grade.DoesNotExist: Grade matching query does not exist.
----------------------------------------------------------------------
Ran 6 tests in 1.527s
FAILED (errors=1)
I can create a Grade
object as the traceback indicates, but my Grade
model depends on five ForeignKeys, one of which has another ForeignKey:
model
class Grade(models.Model):
score = models.CharField(max_length=3, blank=True, default="INC")
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
assessment = models.ForeignKey(
Assessment, on_delete=models.CASCADE, null=True, blank=True)
objective = models.ForeignKey(
Objective, on_delete=models.CASCADE, blank=True)
student = models.ForeignKey(Student, on_delete=models.CASCADE)
cblock = models.ForeignKey(Classroom, on_delete=models.CASCADE, default=1)
time_created = models.DateField(
auto_now=False, auto_now_add=False, default=timezone.now)
working test
from .models import CustomUser, Grade, Student, Course, Classroom, Objective, Assessment, GradeBookSetup
class UpdateSingleGradeTests(TestCase):
def setUp(self):
self.user = CustomUser.objects.create_user(
username='tester',
email='[email protected]',
password='tester123',
is_teacher=True,
)
studentuser = CustomUser.objects.create(
username='student_test'
)
stud = Student.objects.create(
user=studentuser,
student_number='111222'
)
course = Course.objects.create(
user=self.user
)
classroom = Classroom.objects.create(
user=self.user,
course=course
)
# classroom.students.set(stud)
objective = Objective.objects.create(
user=self.user,
course=course
)
assessment = Assessment.objects.create(
user=self.user,
course=course
)
# assessment.objectives.set(objective)
login = self.client.login(username='tester', password='tester123')
grade = Grade.objects.create(
id=3,
user=self.user,
student=stud,
objective=objective,
cblock=classroom,
assessment=assessment
)
gbs = GradeBookSetup.objects.create(
id=1,
user=self.user
)
def test_updatesinglegrade_url_name(self):
response = self.client.get(reverse('gradebook:updatesinglegrade', kwargs={
'assess_pk': 1, 'class_pk': 2, 'grade_pk': 3}))
self.assertEqual(response.status_code, 200)
Does this mean that I need to create 6 objects in the setUp
? I got it work for this test, and I'm wondering if this is something I will have to add to many of my class TestCase
(which have similar objects/models involved)? Or perhaps there is a more efficient way of doing this?
The short answer is, yes, non-null ForeignKey
fields must be populated in tests because they are non-null.
There are several ways to do this:
Call objects.create()
on each model. This can be very tedious and time consuming.
Use a fixture. Creating the JSON files can also be tedious, but fortunately, you can use the Django admin to do data entry and then ./manage.py dumpdata
to generate the JSON.
Use a 3rd party library such as factory-boy
or model-bakery
. These libraries can generate random data for fields that don't really matter for testing other than that they have a value. If you need a specific value for a certain test, then you can provide it.