Need help with writing tests - unexpected fail

I’m working through “Django for Professionals” and this is my first real try at running unit tests. One of my first tests is below and it gives me a very unexpected error.
view

class ClassroomGradebookView(TemplateView):
    """for choosing first gradebook"""
    template_name = "gradebook/gradebookstart.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['classrooms'] = Classroom.objects.all().filter(
		user=self.request.user)
        return context

tests

class ClassroomGradebookTests(TestCase):

    def test_classroomgradebook_url_name(self):
        response = self.client.get(reverse('gradebook:gradebookstart'))
        self.assertEqual(response.status_code, 200)

    def test_classroomgradebook_status_code(self):
        response = self.client.get('/')
        self.assertEqual(response.status_code, 200)

    def test_classroomgradebook_template(self):
        response = self.client.get('/')
        self.assertTemplateUsed(response, 'gradebookstart.html')

    def test_classroomgradebook_correct_html(self):
        response = self.client.get('/')
        self.assertContains(
            response, "Choose which class you'd like to work on")

    def test_classroomgradebook_incorrect_html(self):
        response = self.client.get('/')
        self.assertNotContains(response, 'Not this')

urls

app_name = 'gradebook'
urlpatterns = [
    path('', views.index, name='index'),
   
    path('gradebookstart/', views.ClassroomGradebookView.as_view(),
         name='gradebookstart'),
	]

fail messages

FAIL: test_classroomgradebook_template (gradebook.tests.ClassroomGradebookTests)
AssertionError: False is not true : Template 'gradebookstart.html'
was not a template used to render the response. Actual template(s)
used: home.html, base.html

FAIL: test_classroomgradebook_correct_html (gradebook.tests.ClassroomGradebookTests)
AssertionError: False is not true : Couldn't find 'Choose which
class you'd like to work on' in response

I’m confident that the correct template is loaded when I runserver and go to gradebookstart/. I suppose the second error makes sense because the test isn’t looking at the template I’m expecting it to.

template

{# landing page for the gradebook, pick a classroom to work on. #}
{% extends 'base-g.html' %}
{% load static %}

{% block content %}
<div class="container">
    <div class="row">
        <div class="twelve columns">Choose which class you'd like to work on.</div>
    </div>
    <div class="row">
        <div class="">
            <ul>
            {% for classroom in all_classrooms %}
                <li><a href="{% url 'gradebook:gradebook' classroom.id %}">
                          {{ classroom.classroom_name }}</a>
                </li>
            {% endfor %}
            </ul>
        </div>
    </div>


</div>
{% endblock content %}

Your test is doing a get on “/”, not “gradebookstart/” - you’re not showing what the root url will render.

I get the gist of your response but I’m still having problems. I think I have test written correctly now (not 100% sure - but I have tried a few different versions with different forward slashes).

test

def test_classroomgradebook_template(self):
        response = self.client.get('/gradebook/')
        self.assertTemplateUsed(response, 'gradebookstart.html')

new error

ERROR: test_classroomgradebook_url_name (gradebook.tests.ClassroomGradebookTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\Doug\OneDrive\django\gradebook\gradebook\tests.py", line 42, in
test_classroomgradebook_url_name
response = self.client.get(reverse('gradebook:gradebookstart'))
...
TypeError: 'AnonymousUser' object is not iterable

The basic situation is that somewhere in your templates or your view, you’re trying to do something with either the user object or request.user, but your test isn’t running the request as a user - it’s using the default AnonymousUser.

You haven’t posted the complete traceback, to it’s tough to be 100% sure of anything.

Also, the segment of the template you’ve posted doesn’t have an {% endblock %} tag for the {% block content %}, and you haven’t shown the base-g.html file to know what it’s rendering. So we can’t tell whether there’s something in the template beyond what you’ve shown causing the error.

1 Like

I’m really sorry, I did a terrible job of cutting and pasting. Not only with what you mentioned but also missed some code in the view, I’ve edited my original post. I missed copying/pasting the def get_context_data . I should have known to put in the full traceback.

base-g.html

<!doctype html>
<html lang="en">
<head>
    <!-- Basic Page Needs
  –––––––––––––––––––––––––––––––––––––––––––––––––– -->
  <meta charset="utf-8">
  <title>CRT - {% block body_block %}Classroom Tools{% endblock %}</title>
  <meta name="description" content="">
  <meta name="author" content="">

  <!-- Mobile Specific Metas
  –––––––––––––––––––––––––––––––––––––––––––––––––– -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- FONT
  –––––––––––––––––––––––––––––––––––––––––––––––––– -->
  <link href="http://fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
  <link href='http://fonts.googleapis.com/css?family=Open+Sans|Lato|Droid+Serif|Volkhov:400italic|Droid+Sans' rel='stylesheet' type='text/css'>
  
  <!-- Bootstrap Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">


    <!-- Latest compiled and minified JavaScript -->
  <script src="{% static 'gradebook/js/jquery-3.6.0.min.js' %}"></script>
  <!-- <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
<!--
  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js" integrity="sha384-+YQ4JLhjyBLPDQt//I+STsc9iw4uQqACwlvpslubQzn4u2UU2UFM80nGisd026JF" crossorigin="anonymous"></script>
-->
  
</head>
<body>
  {% block header %}
 
  <div class="container-fluid">
    
    <nav class="navbar navbar-expand-md navbar-light bg-light justify-content-center">
      <div class="d-flex flex-grow-1">
        <span class="w-100 d-lg-none d-block"><!-- hidden spacer to center brand on mobile --></span>
        <a class="navbar-brand d-none d-lg-inline-block" href="#">
          <img class="navbar-brand-two " style="margin-top: -10px;" height="60" src={% static "gradebook/images/vsb-logo.JPG" %}>
        </a>
        <div class="w-100 text-right">
          <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
              <span class="navbar-toggler-icon"></span>
          </button>
        </div>
      </div>
      <div class="collapse navbar-collapse flex-grow-1 text-right" id="navbarNav">
        <ul class="navbar-nav ml-auto flex-nowrap">
          <li class="nav-item active">
           <a class="nav-link" aria-current="page" href={% url 'gradebook:setup' %}>Setup</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href={% url 'gradebook:gradebookstart' %}>Gradebook</a>
           </li> 
          
        </ul>
      </div>
    </nav>
    <ul class="nav">
      <li class="nav-item">
        <a class="nav-link" href={% url 'home' %}>Home</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href={% url 'admin:index' %}>Admin</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href={% url 'gradebook:objectivecourse' %}>Learning Goals</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href={% url 'gradebook:assesscourse' %}>Assessments</a>
      </li>
    </ul>
    </div>
    <div class="container">
      <div class="dropdown">
        <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
          Classroom
        </button>
        <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
          {% for classroom in all_classrooms %}
          <li><a href="{% url 'gradebook:gradebook' classroom.id %}">{{ classroom.classroom_name }}</a></li>
          {% endfor %}
        </ul>
      </div>
    </div>
 
  </div>
  {% endblock %}
  <main>
    {% block content %}{% endblock content %}
    {% block extra_js %}{% endblock extra_js %}
  </main>
</body>
</html>

traceback

======================================================================
ERROR: test_classroomgradebook_url_name (gradebook.tests.ClassroomGradebookTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\Doug\OneDrive\django\gradebook\gradebook\tests.py", line 42, in test_classroomgradebook_url_name
    response = self.client.get(reverse('gradebook:gradebookstart'))
  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\views\generic\base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\views\generic\base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\views\generic\base.py", line 159, in get
    context = self.get_context_data(**kwargs)
  File "C:\Users\Doug\OneDrive\django\gradebook\gradebook\views.py", line 209, in get_context_data
    context['classrooms'] = Classroom.objects.all().filter(
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\query.py", line 941, in filter
    return self._filter_or_exclude(False, args, kwargs)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\query.py", line 961, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\query.py", line 968, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\sql\query.py", line 1393, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\sql\query.py", line 1412, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\sql\query.py", line 1320, in build_filter
    self.check_related_objects(join_info.final_field, value, join_info.opts)
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\db\models\sql\query.py", line 1149, in check_related_objects
    for v in value:
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\utils\functional.py", line 247, in inner
    return func(self._wrapped, *args)
TypeError: 'AnonymousUser' object is not iterable

======================================================================
FAIL: test_classroomgradebook_correct_html (gradebook.tests.ClassroomGradebookTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\Doug\OneDrive\django\gradebook\gradebook\tests.py", line 55, in test_classroomgradebook_correct_html
    self.assertContains(
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\testcases.py", line 471, in assertContains
    self.assertTrue(real_count != 0, msg_prefix + "Couldn't find %s in response" % text_repr)
AssertionError: False is not true : Couldn't find 'Choose which class you'd like to work on' in response

======================================================================
FAIL: test_classroomgradebook_template (gradebook.tests.ClassroomGradebookTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\Doug\OneDrive\django\gradebook\gradebook\tests.py", line 51, in test_classroomgradebook_template
    self.assertTemplateUsed(response, 'gradebookstart.html')
  File "C:\Users\Doug\.virtualenvs\gradebook-6RQSg7dk\lib\site-packages\django\test\testcases.py", line 656, in assertTemplateUsed
    self.fail(msg_prefix + "No templates used to render the response")
AssertionError: No templates used to render the response
----------------------------------------------------------------------
Ran 5 tests in 0.048s

FAILED (failures=2, errors=1)
Destroying test database for alias 'default'...

I can see how there is an issue with the user because the get_context_data is hitting the db, but I don’t know how to deal with it. I’ve read about using a setup like:

def setup(self):
    user = CustomUser.objects.create_user(
        username='tester',
        email='tester@email.com',
        password='tester123'
    )

But adding this setup did not change the error message. I think there is a hurdle for me to get over with writing tests, obviously I haven’t wrapped my head around it yet.

Using setUp() should work, but it looks for a method called setUp() with a capital-U, where it looks like your method is lowercase. → See setUp() doc.

Additionally, just creating the user might not be sufficient. Just before you call self.client.get, you’ll want to force the client to be logged in as that user, with e.g.

self.client.force_login(CustomUser.objects.get(username='tester'))

→ See force_login for details on that.

2 Likes

Using setUp() should work, but it looks for a method called setUp() with a capital-U, where it looks like your method is lowercase. → See setUp() doc.

Great, that worked. I didn’t need to force_login.

Your test is doing a get on “/”, not “gradebookstart/” - you’re not showing what the root url will render.

I modified my template, correct_html and incorrect_html tests to use the following and it now works.

url = reverse('gradebook:gradebookstart')
response = self.client.get(url)