Een navigatiemenu testen in Wagtail met Factory Boy

Een navigatiemenu hangt af van de inhoud van een site. In deze tutorial maken we de inhoud van een Wagtail-site met Factory Boy en testen we het navigatiemenu.

14 juli 2020 20:43
Thema's: Navigatie Testen

In een eerdere tutorial hebben we een navigatiemenu gemaakt dat zowel Wagtail-pagina's als pagina's buiten de Wagtail-boom kan verwerken, meertalig is en via admin kan worden bewerkt. In een andere tutorial hebben we een fabriek opgezet voor het testen van Wagtail-pagina's. We gaan dit samenbrengen om het navigatiemenu te testen. Omdat we al een paginafabriek hebben opgezet, kunnen we wat inhoud voor de site maken. Laten we wat inhoud opzetten voor onze tests: een site, een startpagina, een artikelindexpagina en twee artikelpagina's. Merk op dat we de velden show_in_menus instellen op True. Maak in onze directory / tests een bestand test_menus.py aan:

from .factories import HomePageFactory, ArticlePageFactory, ArticleIndexPageFactory
from django.test import TestCase
from wagtail.core.models import Page, Site

class TestMenus(TestCase):

    @classmethod
    def setUpTestData(cls):
        cls.site = Site.objects.create(is_default_site=True, root_page=Page.get_first_root_node())
        cls.homepage = HomePageFactory()
        cls.articleindexpage = ArticleIndexPageFactory(parent=cls.homepage)
        cls.articlepage1 = ArticlePageFactory(parent=cls.articleindexpage, show_in_menus=True)
        cls.articlepage2 = ArticlePageFactory(parent=cls.articleindexpage, show_in_menus=True)

Voordat we een menu en menu-items kunnen maken, moeten we er een fabriek voor maken in ons bestand /tests/factories.py. De fabriek voor het menu is eenvoudig:

from ..models import Menu

class MenuFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Menu

    title = factory.Sequence(lambda n: 'Menu {0}'.format(n))

We hebben het gebruik van Sequence al uitgelegd om automatisch een titel te genereren. De menu-item fabriek kan als volgt worden gedefinieerd:

from ..models import MenuItem

class MenuItemFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = MenuItem

    menu = factory.SubFactory(MenuFactory)

De foreign key-relatie tussen het menu en het menu-item wordt tot stand gebracht via een SubFactory. In de testopstelling in het bestand test_menus.py maken we een menu en drie menu-items met een paar velden die zijn ingesteld op verschillende waarden:

cls.menu = MenuFactory()
# for the menu-items we need to establish a sort order (class Orderable)
cls.menuitem_ordinary = MenuItemFactory(menu=cls.menu, sort_order=1, title="Ordinary",
                                        link_url='/ordinary/', show_when='always')
cls.menuitem_guest = MenuItemFactory(menu=cls.menu, sort_order=2, title="Guest", link_url='/guest/',
                                     show_when='not_logged_in')
cls.menuitem_articlepage1 = MenuItemFactory(menu=cls.menu, sort_order=3, link_page=cls.articlepage1)

Aangezien het model MenuItem is afgeleid van de klasse Orderable, voegen we handmatig een sort_order toe. Verder hebben we een titel, een link_url en verschillende waarden ingesteld voor het veld show_when, om te gebruiken in onze tests. Tijd om onze eerste test te schrijven: controleer of onze functie get_menu correct een menu genereert voor een pagina met kinderen:

def test_get_menu_of_page(self):
    menu = get_menu(slug=None, page=self.articleindexpage, logged_in=True)
    self.assertEqual(menu[0]['title'], self.articlepage1.title)
    self.assertEqual(menu[0]['url'], self.articlepage1.url)
    self.assertEqual(menu[0]['page'].title, self.articlepage1.title)
    self.assertEqual(len(menu), 2)

We voeren de test uit met:

python3 manage.py test cms.tests.test_menus

Tot nu toe alles in orde. Laten we nu een handgemaakt menu testen:

from django.utils import translation

def test_get_handmade_menu(self):
    menu = get_menu(self.menu.slug, None, True)
    self.assertEqual(menu[0]['title'], 'Ordinary')
    # the expected url is in the current language
    expected_url = '/' + translation.get_language() + '/ordinary/'
    self.assertEqual(menu[0]['url'], expected_url)

De functie get_menu genereert het menu vanuit de slug van de menu-instantie, die automatisch wordt gegenereerd op basis van de titel in ons model. Het eerste menu-item zou dan de titel van het eerste menu-item moeten hebben. Omdat onze site meertalig is, verwachten we dat de url wordt voorafgegaan door de huidige taalcode, dus gebruiken we de functie get_language()om dit te controleren. Testen of de functie get_menu de parameter logged_in oppikt, is eenvoudig:

def test_get_menu_logged_in_or_not(self):
    menu = get_menu(self.menu.slug, None, True)
    # menu should only have two items
    self.assertEqual(len(menu), 2)
    menu = get_menu(self.menu.slug, None, False)
    self.assertEqual(len(menu), 3)

We willen wat meer tests toevoegen over hoe get_menu met talen omgaat. Daarvoor introduceren we in onze setup een code van een vreemde taal en een pagina in een vreemde taal:

from django.conf import settings
from wagtailtrans.models import Language, TranslatablePage

cls.foreign_language_code = [code for code, lang in settings.LANGUAGES if code != settings.LANGUAGE_CODE][0]
# note: Wagtailtrans automatically creates a language tree for every language that is defined
cls.foreign_language = Language.objects.get_or_create(code=cls.foreign_language_code)[0]
cls.foreign_articlepage1 = TranslatablePage.objects.get(language=cls.foreign_language, canonical_page=cls.articlepage1)

We halen de code van de vreemde taal op uit onze instellingen. Wagtailtrans heeft een speciaal model Language, waarvan we een exemplaar nodig hebben. Wanneer een pagina wordt aangemaakt (in ons geval articlepage1), genereert Wagtailtrans automatisch een pagina voor elke taal in instellingen. Daarom kunnen we een vreemde-taalversie van die pagina ophalen door de velden language en canonical_page te gebruiken. Nu kunnen we de methode trans_page() en trans_url() van het MenuItem-model testen:

def test_menuitem_trans_page_for_foreign_language(self):
self.assertEqual(self.menuitem_articlepage1.trans_page(self.foreign_language_code).url, self.foreign_articlepage1.url)

def test_menuitem_trans_page_for_canonical_language(self):
self.assertEqual(self.menuitem_articlepage1.trans_page(settings.LANGUAGE_CODE).url, self.articlepage1.url)

def test_menuitem_trans_url_method(self):
self.assertEqual(self.menuitem_articlepage1.trans_url(self.foreign_language_code), self.foreign_articlepage1.url)

Voer coverage uit om te controleren hoeveel van de code we tot nu toe hebben gedekt:

coverage run --source=cms manage.py test cms
coverage html

Onze templatetags zijn voldoende gedekt. Tijd om verder te gaan met het testen van de talen in views.py.

Reageer op dit artikel (log eerst in of bevestig hieronder met naam en email)