Merge branch 'Aufinal/simplify_tests' into 'master'

Utilitaire de tests simplifié

See merge request klub-dev-ens/gestioCOF!421
This commit is contained in:
Ludovic Stephan 2020-05-15 16:12:47 +02:00
commit 90fc6aa3e7
9 changed files with 328 additions and 261 deletions

View file

@ -4,7 +4,7 @@ from django.conf import settings
from django.core.management import call_command
from django.utils import timezone
from shared.tests.testcases import ViewTestCaseMixin
from shared.tests.mixins import ViewTestCaseMixin
from ..models import CategorieSpectacle, Salle, Spectacle, Tirage
from .utils import create_user

View file

@ -8,7 +8,7 @@ from django.urls import reverse
from django.utils import formats, timezone
from ..models import Participant, Tirage
from .testcases import BdATestHelpers, BdAViewTestCaseMixin
from .mixins import BdATestHelpers, BdAViewTestCaseMixin
User = get_user_model()

View file

@ -1,4 +1,3 @@
import csv
from unittest import mock
from django.contrib.auth import get_user_model
@ -14,6 +13,7 @@ from events.models import (
OptionChoice,
Registration,
)
from shared.tests.mixins import CSVResponseMixin
User = get_user_model()
@ -70,7 +70,7 @@ class CSVExportAccessTest(MessagePatch, TestCase):
self.assertEqual(r.status_code, 403)
class CSVExportContentTest(MessagePatch, TestCase):
class CSVExportContentTest(MessagePatch, CSVResponseMixin, TestCase):
def setUp(self):
super().setUp()
@ -90,13 +90,25 @@ class CSVExportContentTest(MessagePatch, TestCase):
def test_simple_event(self):
self.event.subscribers.set([self.u1, self.u2])
participants = self.client.get(self.url).content.decode("utf-8")
participants = [
line for line in csv.reader(participants.split("\n")) if line != []
]
self.assertEqual(len(participants), 3)
self.assertEqual(participants[1], ["toto_foo", "toto@a.b", "toto", "foo"])
self.assertEqual(participants[2], ["titi_bar", "titi@a.b", "titi", "bar"])
response = self.client.get(self.url)
self.assertCSVEqual(
response,
[
{
"username": "toto_foo",
"prénom": "toto",
"nom de famille": "foo",
"email": "toto@a.b",
},
{
"username": "titi_bar",
"prénom": "titi",
"nom de famille": "bar",
"email": "titi@a.b",
},
],
)
def test_complex_event(self):
registration = Registration.objects.create(event=self.event, user=self.u1)
@ -127,15 +139,18 @@ class CSVExportContentTest(MessagePatch, TestCase):
field=field, registration=registration, content="hello"
)
participants = self.client.get(self.url).content.decode("utf-8")
participants = list(csv.reader(participants.split("\n")))
toto_registration = participants[1]
# This is not super nice, but it makes the test deterministic.
if toto_registration[5] == "f & d":
toto_registration[5] = "d & f"
self.assertEqual(
["toto_foo", "toto@a.b", "toto", "foo", "a", "d & f", "hello"],
toto_registration,
response = self.client.get(self.url)
self.assertCSVEqual(
response,
[
{
"username": "toto_foo",
"prénom": "toto",
"nom de famille": "foo",
"email": "toto@a.b",
"abc": "a",
"def": "d & f",
"remarks": "hello",
}
],
)

View file

@ -38,7 +38,7 @@ def participants_csv(request, event_id):
# Options
all_choices = registration.options_choices.values_list("choice", flat=True)
options_choices = [
" & ".join(all_choices.filter(option__id=id))
" & ".join(all_choices.filter(option__id=id).order_by("id"))
for id in event.options.values_list("id", flat=True).order_by("id")
]
row += options_choices

View file

@ -0,0 +1,74 @@
from gestioncof.models import Event
from shared.tests.mixins import (
CSVResponseMixin,
ViewTestCaseMixin as BaseViewTestCaseMixin,
)
from .utils import create_member, create_staff, create_user
class MegaHelperMixin(CSVResponseMixin):
"""
Mixin pour aider aux tests du MEGA: création de l'event et de plusieurs
inscriptions, avec options et commentaires.
"""
def setUp(self):
super().setUp()
u1 = create_user("u1")
u1.first_name = "first"
u1.last_name = "last"
u1.email = "user@mail.net"
u1.save()
u1.profile.phone = "0123456789"
u1.profile.departement = "Dept"
u1.profile.comments = "profile.comments"
u1.profile.save()
u2 = create_user("u2")
u2.profile.save()
m = Event.objects.create(title="MEGA 2018")
cf1 = m.commentfields.create(name="Commentaires")
cf2 = m.commentfields.create(name="Comment Field 2", fieldtype="char")
option_type = m.options.create(name="Orga ? Conscrit ?")
choice_orga = option_type.choices.create(value="Orga")
choice_conscrit = option_type.choices.create(value="Conscrit")
mr1 = m.eventregistration_set.create(user=u1)
mr1.options.add(choice_orga)
mr1.comments.create(commentfield=cf1, content="Comment 1")
mr1.comments.create(commentfield=cf2, content="Comment 2")
mr2 = m.eventregistration_set.create(user=u2)
mr2.options.add(choice_conscrit)
self.u1 = u1
self.u2 = u2
self.m = m
self.choice_orga = choice_orga
self.choice_conscrit = choice_conscrit
class ViewTestCaseMixin(BaseViewTestCaseMixin):
"""
TestCase extension to ease testing of cof views.
Most information can be found in the base parent class doc.
This class performs some changes to users management, detailed below.
During setup, three users are created:
- 'user': a basic user without any permission,
- 'member': (profile.is_cof is True),
- 'staff': (profile.is_cof is True) && (profile.is_buro is True).
"""
def get_users_base(self):
return {
"user": create_user("user"),
"member": create_member("member"),
"staff": create_staff("staff"),
}

View file

@ -1,4 +1,3 @@
import csv
import os
import uuid
from datetime import timedelta
@ -16,7 +15,8 @@ from django.urls import reverse
from bda.models import Salle, Tirage
from gestioncof.models import CalendarSubscription, Club, Event, Survey, SurveyAnswer
from gestioncof.tests.testcases import ViewTestCaseMixin
from gestioncof.tests.mixins import MegaHelperMixin, ViewTestCaseMixin
from shared.tests.mixins import CSVResponseMixin, ICalMixin, MockLDAPMixin
from shared.views.autocomplete import Clipper
from .utils import create_member, create_root, create_user
@ -227,7 +227,7 @@ class RegistrationFormViewTests(ViewTestCaseMixin, TestCase):
auth_forbidden = [None, "user", "member"]
def test_empty(self):
r = self.client.get(self.t_urls[0])
r = self.client.get(self.reversed_urls[0])
self.assertIn("user_form", r.context)
self.assertIn("profile_form", r.context)
@ -240,7 +240,7 @@ class RegistrationFormViewTests(ViewTestCaseMixin, TestCase):
u.last_name = "last"
u.save()
r = self.client.get(self.t_urls[1])
r = self.client.get(self.reversed_urls[1])
self.assertIn("user_form", r.context)
self.assertIn("profile_form", r.context)
@ -252,7 +252,7 @@ class RegistrationFormViewTests(ViewTestCaseMixin, TestCase):
self.assertEqual(user_form["last_name"].initial, "last")
def test_clipper(self):
r = self.client.get(self.t_urls[2])
r = self.client.get(self.reversed_urls[2])
self.assertIn("user_form", r.context)
self.assertIn("profile_form", r.context)
@ -267,7 +267,7 @@ class RegistrationFormViewTests(ViewTestCaseMixin, TestCase):
@override_settings(LDAP_SERVER_URL="ldap_url")
class RegistrationAutocompleteViewTests(ViewTestCaseMixin, TestCase):
class RegistrationAutocompleteViewTests(MockLDAPMixin, ViewTestCaseMixin, TestCase):
url_name = "cof.registration.autocomplete"
url_expected = "/autocomplete/registration"
@ -462,7 +462,7 @@ class UserAutocompleteViewTests(ViewTestCaseMixin, TestCase):
self.assertEqual(r.status_code, 200)
class ExportMembersViewTests(ViewTestCaseMixin, TestCase):
class ExportMembersViewTests(CSVResponseMixin, ViewTestCaseMixin, TestCase):
url_name = "cof.membres_export"
url_expected = "/export/members"
@ -482,70 +482,27 @@ class ExportMembersViewTests(ViewTestCaseMixin, TestCase):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
data = list(csv.reader(r.content.decode("utf-8").split("\n")[:-1]))
expected = [
self.assertCSVEqual(
r,
[
str(u1.pk),
"member",
"first",
"last",
"user@mail.net",
"0123456789",
"1A",
"Dept",
"normalien",
[
str(u1.pk),
"member",
"first",
"last",
"user@mail.net",
"0123456789",
"1A",
"Dept",
"normalien",
],
[str(u2.pk), "staff", "", "", "", "", "1A", "", "normalien"],
],
[str(u2.pk), "staff", "", "", "", "", "1A", "", "normalien"],
]
# Sort before checking equality, the order of the output of csv.reader
# does not seem deterministic
expected.sort(key=lambda row: int(row[0]))
data.sort(key=lambda row: int(row[0]))
self.assertListEqual(data, expected)
)
class MegaHelpers:
def setUp(self):
super().setUp()
u1 = create_user("u1")
u1.first_name = "first"
u1.last_name = "last"
u1.email = "user@mail.net"
u1.save()
u1.profile.phone = "0123456789"
u1.profile.departement = "Dept"
u1.profile.comments = "profile.comments"
u1.profile.save()
u2 = create_user("u2")
u2.profile.save()
m = Event.objects.create(title="MEGA 2018")
cf1 = m.commentfields.create(name="Commentaires")
cf2 = m.commentfields.create(name="Comment Field 2", fieldtype="char")
option_type = m.options.create(name="Orga ? Conscrit ?")
choice_orga = option_type.choices.create(value="Orga")
choice_conscrit = option_type.choices.create(value="Conscrit")
mr1 = m.eventregistration_set.create(user=u1)
mr1.options.add(choice_orga)
mr1.comments.create(commentfield=cf1, content="Comment 1")
mr1.comments.create(commentfield=cf2, content="Comment 2")
mr2 = m.eventregistration_set.create(user=u2)
mr2.options.add(choice_conscrit)
self.u1 = u1
self.u2 = u2
self.m = m
self.choice_orga = choice_orga
self.choice_conscrit = choice_conscrit
class ExportMegaViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
class ExportMegaViewTests(MegaHelperMixin, ViewTestCaseMixin, TestCase):
url_name = "cof.mega_export"
url_expected = "/export/mega"
@ -556,8 +513,8 @@ class ExportMegaViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertListEqual(
self.load_from_csv_response(r),
self.assertCSVEqual(
r,
[
[
"u1",
@ -574,7 +531,7 @@ class ExportMegaViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
)
class ExportMegaOrgasViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
class ExportMegaOrgasViewTests(MegaHelperMixin, ViewTestCaseMixin, TestCase):
url_name = "cof.mega_export_orgas"
url_expected = "/export/mega/orgas"
@ -586,8 +543,8 @@ class ExportMegaOrgasViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertListEqual(
self.load_from_csv_response(r),
self.assertCSVEqual(
r,
[
[
"u1",
@ -603,7 +560,7 @@ class ExportMegaOrgasViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
)
class ExportMegaParticipantsViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
class ExportMegaParticipantsViewTests(MegaHelperMixin, ViewTestCaseMixin, TestCase):
url_name = "cof.mega_export_participants"
url_expected = "/export/mega/participants"
@ -614,13 +571,12 @@ class ExportMegaParticipantsViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertListEqual(
self.load_from_csv_response(r),
[["u2", "", "", "", "", str(self.u2.pk), "", ""]],
self.assertCSVEqual(
r, [["u2", "", "", "", "", str(self.u2.pk), "", ""]],
)
class ExportMegaRemarksViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
class ExportMegaRemarksViewTests(MegaHelperMixin, ViewTestCaseMixin, TestCase):
url_name = "cof.mega_export_remarks"
url_expected = "/export/mega/avecremarques"
@ -631,8 +587,8 @@ class ExportMegaRemarksViewTests(MegaHelpers, ViewTestCaseMixin, TestCase):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertListEqual(
self.load_from_csv_response(r),
self.assertCSVEqual(
r,
[
[
"u1",
@ -815,7 +771,7 @@ class CalendarViewTests(ViewTestCaseMixin, TestCase):
self.assertEqual(r.status_code, 200)
class CalendarICSViewTests(ViewTestCaseMixin, TestCase):
class CalendarICSViewTests(ICalMixin, ViewTestCaseMixin, TestCase):
url_name = "calendar.ics"
auth_user = None

View file

@ -1,24 +0,0 @@
from shared.tests.testcases import ViewTestCaseMixin as BaseViewTestCaseMixin
from .utils import create_member, create_staff, create_user
class ViewTestCaseMixin(BaseViewTestCaseMixin):
"""
TestCase extension to ease testing of cof views.
Most information can be found in the base parent class doc.
This class performs some changes to users management, detailed below.
During setup, three users are created:
- 'user': a basic user without any permission,
- 'member': (profile.is_cof is True),
- 'staff': (profile.is_cof is True) && (profile.is_buro is True).
"""
def get_users_base(self):
return {
"user": create_user("user"),
"member": create_member("member"),
"staff": create_staff("staff"),
}

View file

@ -1,12 +1,11 @@
import json
import os
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.test import Client, TestCase
from django.test import TestCase
from django.urls import reverse
from gestioncof.tests.testcases import ViewTestCaseMixin
from gestioncof.tests.mixins import ViewTestCaseMixin
from .utils import (
PetitCoursTestHelpers,

View file

@ -13,6 +13,130 @@ from django.utils.functional import cached_property
User = get_user_model()
class MockLDAPMixin:
"""
Mixin pour simuler un appel à un serveur LDAP (e.g., celui de l'ENS) dans des
tests unitaires. La réponse est une instance d'une classe Entry, qui simule
grossièrement l'interface de ldap3.
Cette classe patche la méthode magique `__enter__`, le code correspondant doit donc
appeler `with Connection(*args, **kwargs) as foo` pour que le test fonctionne.
"""
def mockLDAP(self, results):
class Elt:
def __init__(self, value):
self.value = value
class Entry:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, Elt(v))
results_as_ldap = [Entry(uid=uid, cn=name) for uid, name in results]
mock_connection = mock.MagicMock()
mock_connection.entries = results_as_ldap
# Connection is used as a context manager.
mock_context_manager = mock.MagicMock()
mock_context_manager.return_value.__enter__.return_value = mock_connection
patcher = mock.patch(
"shared.views.autocomplete.Connection", new=mock_context_manager
)
patcher.start()
self.addCleanup(patcher.stop)
return mock_connection
class CSVResponseMixin:
"""
Mixin pour manipuler des réponses données via CSV. Deux choix sont possibles:
- si `as_dict=False`, convertit le CSV en une liste de listes (une liste par ligne)
- si `as_dict=True`, convertit le CSV en une liste de dicts, avec les champs donnés
par la première ligne du CSV.
"""
def _load_from_csv_response(self, r, as_dict=False, **reader_kwargs):
content = r.content.decode("utf-8")
# la dernière ligne du fichier CSV est toujours vide
content = content.split("\n")[:-1]
if as_dict:
content = csv.DictReader(content, **reader_kwargs)
# en python3.7, content est une liste d'OrderedDicts
return list(map(dict, content))
else:
content = csv.reader(content, **reader_kwargs)
return list(content)
def assertCSVEqual(self, response, expected):
if type(expected[0]) == list:
as_dict = False
elif type(expected[0]) == dict:
as_dict = True
else:
raise AssertionError(
"Unsupported type in `assertCSVEqual`: "
"%(expected)s is not of type `list` nor `dict` !"
% {"expected": str(expected[0])}
)
content = self._load_from_csv_response(response, as_dict=as_dict)
self.assertCountEqual(content, expected)
class ICalMixin:
"""
Mixin pour manipuler des iCalendars. Permet de tester l'égalité entre
in iCal d'une part, et une liste d'évènements (représentés par des dicts)
d'autre part.
"""
def _test_event_equal(self, event, exp):
"""
Les éléments du dict peuvent être de deux types:
- un tuple `(getter, expected_value)`, auquel cas on teste l'égalité
`getter(event[key]) == value)`;
- une variable `value` de n'importe quel autre type, auquel cas on teste
`event[key] == value`.
"""
for key, value in exp.items():
if isinstance(value, tuple):
getter = value[0]
v = value[1]
else:
getter = lambda v: v
v = value
# dans un iCal, les fields sont en majuscules
if getter(event[key.upper()]) != v:
return False
return True
def _find_event(self, ev, l):
for i, elt in enumerate(l):
if self._test_event_equal(ev, elt):
return i
return None
def assertCalEqual(self, ical_content, expected):
remaining = expected.copy()
unexpected = []
cal = icalendar.Calendar.from_ical(ical_content)
for ev in cal.walk("vevent"):
i_found = self._find_event(ev, remaining)
if i_found is not None:
remaining.pop(i_found)
else:
unexpected.append(ev)
self.assertListEqual(remaining, [])
self.assertListEqual(unexpected, [])
class TestCaseMixin:
def assertForbidden(self, response):
"""
@ -91,140 +215,69 @@ class TestCaseMixin:
else:
self.assertEqual(actual, expected)
def mockLDAP(self, results):
class Elt:
def __init__(self, value):
self.value = value
class Entry:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, Elt(v))
results_as_ldap = [Entry(uid=uid, cn=name) for uid, name in results]
mock_connection = mock.MagicMock()
mock_connection.entries = results_as_ldap
# Connection is used as a context manager.
mock_context_manager = mock.MagicMock()
mock_context_manager.return_value.__enter__.return_value = mock_connection
patcher = mock.patch(
"shared.views.autocomplete.Connection", new=mock_context_manager
)
patcher.start()
self.addCleanup(patcher.stop)
return mock_connection
def load_from_csv_response(self, r):
decoded = r.content.decode("utf-8")
return list(csv.reader(decoded.split("\n")[:-1]))
def _test_event_equal(self, event, exp):
for k, v_desc in exp.items():
if isinstance(v_desc, tuple):
v_getter = v_desc[0]
v = v_desc[1]
else:
v_getter = lambda v: v
v = v_desc
if v_getter(event[k.upper()]) != v:
return False
return True
def _find_event(self, ev, l):
for i, elt in enumerate(l):
if self._test_event_equal(ev, elt):
return elt, i
return False, -1
def assertCalEqual(self, ical_content, expected):
remaining = expected.copy()
unexpected = []
cal = icalendar.Calendar.from_ical(ical_content)
for ev in cal.walk("vevent"):
found, i_found = self._find_event(ev, remaining)
if found:
remaining.pop(i_found)
else:
unexpected.append(ev)
self.assertListEqual(unexpected, [])
self.assertListEqual(remaining, [])
class ViewTestCaseMixin(TestCaseMixin):
"""
TestCase extension to ease tests of kfet views.
Utilitaire pour automatiser certains tests sur les vues Django.
Création d'utilisateurs
------------------------
# Données de base
On crée dans tous les cas deux utilisateurs : un utilisateur normal "user",
et un superutilisateur "root", avec un mot de passe identique au username.
Urls concerns
-------------
# Accès et utilisateurs supplémentaires
Les utilisateurs créés sont accessibles dans le dict `self.users`, qui associe
un label à une instance de User.
# Basic usage
Pour rajouter des utilisateurs supplémentaires (et s'assurer qu'ils sont
disponibles dans `self.users`), on peut redéfinir la fonction `get_users_extra()`,
qui doit renvoyer aussi un dict <label: User instance>.
Attributes:
url_name (str): Name of view under test, as given to 'reverse'
function.
url_args (list, optional): Will be given to 'reverse' call.
url_kwargs (dict, optional): Same.
url_expcted (str): What 'reverse' should return given previous
attributes.
Misc QoL
------------------------
Pour éviter une erreur de login (puisque les messages de Django ne sont pas
disponibles), les messages de bienvenue de GestioCOF sont patchés.
Un attribut `self.now` est fixé au démarrage, pour être donné comme valeur
de retour à un patch local de `django.utils.timezone.now`. Cela permet de
tester des dates/heures de manière robuste.
View url can then be accessed at the 'url' attribute.
Test d'URLS
------------------------
# Advanced usage
# Usage basique
Teste que l'URL générée par `reverse` correspond bien à l'URL théorique.
Attributs liés :
- `url_name` : nom de l'URL qui sera donné à `reverse`,
- `url_expected` : URL attendue en retour.
- (optionnels) `url_args` et `url_kwargs` : arguments de l'URL pour `reverse`.
If multiple combinations of url name, args, kwargs can be used for a view,
it is possible to define 'urls_conf' attribute. It must be a list whose
each item is a dict defining arguments for 'reverse' call ('name', 'args',
'kwargs' keys) and its expected result ('expected' key).
# Usage avancé
On peut tester plusieurs URLs pour une même vue, en redéfinissant la fonction
`urls_conf()`. Cette fonction doit retourner une liste de dicts, avec les clés
suivantes : `name`, `args`, `kwargs`, `expected`.
The reversed urls can be accessed at the 't_urls' attribute.
# Accès aux URLs générées
Dans le cas d'usage basique, l'attribut `self.url` contient l'URL de la vue testée
(telle que renvoyée par `reverse()`). Si plusieurs URLs sont définies dans
`urls_conf()`, elles sont accessibles par la suite dans `self.reversed_urls`.
Authentification
------------------------
Si l'attribut `auth_user` est dans `self.users`, l'utilisateur correspondant
est authentifié avant chaque test (cela n'empêche bien sûr pas de login un autre
utilisateur à la main).
Users concerns
--------------
During setup, the following users are created:
- 'user': a basic user without any permission,
- 'root': a superuser, account trigramme: 200.
Their password is their username.
One can create additionnal users with 'get_users_extra' method, or prevent
these users to be created with 'get_users_base' method. See these two
methods for further informations.
By using 'register_user' method, these users can then be accessed at
'users' attribute by their label.
A user label can be given to 'auth_user' attribute. The related user is
then authenticated on self.client during test setup. Its value defaults to
'None', meaning no user is authenticated.
Automated tests
---------------
# Url reverse
Based on url-related attributes/properties, the test 'test_urls' checks
that expected url is returned by 'reverse' (once with basic url usage and
each for advanced usage).
# Forbidden responses
The 'test_forbidden' test verifies that each user, from labels of
'auth_forbidden' attribute, can't access the url(s), i.e. response should
be a 403, or a redirect to login view.
Tested HTTP requests are given by 'http_methods' attribute. Additional data
can be given by defining an attribute '<method(lowercase)>_data'.
Test de restrictions d'accès
------------------------
L'utilitaire vérifie automatiquement que certains utilisateurs n'ont pas accès à la
vue. Plus spécifiquement, sont testés toutes les méthodes dans `self.http_methods`
et tous les utilisateurs dans `self.auth_forbidden`. Pour rappel, l'utilisateur
`None` sert à tester la vue sans authentification.
On peut donner des paramètres GET/POST/etc. aux tests en définissant un attribut
<methode>_data.
TODO (?): faire pareil pour vérifier les GET/POST classiques (code 200)
"""
url_name = None
@ -239,19 +292,13 @@ class ViewTestCaseMixin(TestCaseMixin):
"""
Warning: Do not forget to call super().setUp() in subclasses.
"""
# Signals handlers on login/logout send messages.
# Due to the way the Django' test Client performs login, this raise an
# error. As workaround, we mock the Django' messages module.
patcher_messages = mock.patch("gestioncof.signals.messages")
patcher_messages.start()
self.addCleanup(patcher_messages.stop)
# A test can mock 'django.utils.timezone.now' and give this as return
# value. E.g. it is useful if the test checks values of 'auto_now' or
# 'auto_now_add' fields.
self.now = timezone.now()
# Register of User instances.
self.users = {}
for label, user in dict(self.users_base, **self.users_extra).items():
@ -322,7 +369,7 @@ class ViewTestCaseMixin(TestCaseMixin):
]
@property
def t_urls(self):
def reversed_urls(self):
return [
reverse(
url_conf["name"],
@ -335,16 +382,16 @@ class ViewTestCaseMixin(TestCaseMixin):
@property
def url(self):
return self.t_urls[0]
return self.reversed_urls[0]
def test_urls(self):
for url, conf in zip(self.t_urls, self.urls_conf):
for url, conf in zip(self.reversed_urls, self.urls_conf):
self.assertEqual(url, conf["expected"])
def test_forbidden(self):
for method in self.http_methods:
for user in self.auth_forbidden:
for url in self.t_urls:
for url in self.reversed_urls:
self.check_forbidden(method, url, user)
def check_forbidden(self, method, url, user=None):