Kees Hink
Kees Hink
25 april 2018

Fixtures for custom Wagtail Page models

This blog post describes how to create custom content for tests in Wagtail.

Why test

One important aspect of developing a new platform is creating automated tests. Having all custom-made code tested makes it unlikely that a code change (new feature, bugfix) will unexpectedly break something else.

Creating content for tests

To have something to test with, we need to populate the site with some content.

Let’s say your custom homepage should list all news items.

Then probably you’d like your test setup to create that homepage for you, and replace Wagtail’s default homepage with it. You’d also create some news items, and then you could test if these are rendered if you go to the site root.

Fixtures and loaddata

Django’s loaddata command facilitates creating content from “fixtures”: JSON or Yaml files.

For example, to create a Wagtail Page, you can do this in your fixture myfixture.json:

[
  {
    "model": "wagtailcore.page",
    "pk": 3,
    "fields": {
      "title": "My Customer's Homepage",
      "depth": 2
    }
]

Then you would run manage.py loaddata myfixture.json and the content would be created for you.

Creating content in this manner is also a nice way to populate a development environment. After setting up the database and running the migrations, you could use fixtures to set up all the content necessary for a minimal working website for development or user testing.

Custom Page model

But in our setup, we have a custom Homepage model, which subclasses wagtailcore.models.Page. Wagtail creates entries for both models, with the same pk. This should be reflected in our fixture, as Matt Wescott pointed out to me on Slack.

So we end up with this in our homepage.json fixture:

[
  {
    "model": "wagtailcore.page",
    "pk": 3,
    "fields": {
      "title": "My Customer's Homepage",
      "content_type": ["website", "homepage"],
      "depth": 2
    }
  },
  {
    "model": "website.homepage",
    "pk": 3,
    "fields": {}  // website.homepage field values go here
  },
  {
    "model": "wagtailcore.site",
    "pk": 1,
    "fields": {
      "site_name": "DEV",
      "is_default_site": true,
      "root_page": 3
    }
  }
]

Note that we define a wagtailcore.page which has a different content_type: website.homepage.

We still need a separate entry for that content type:
"model": "website.homepage". This is matched on pk with the previous Wagtail Page.

In the last step, we find the Site object and change its root_page to the one we just created.

The test

Our test first calls loaddata:

@pytest.fixture(scope='session')
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        call_command('loaddata', 'homepage.json')

Now we can test if a call on the site root gives us our custom homepage:

@pytest.mark.django_db
def test_homepage(client):
    """Test the custom homepage."""
    response = client.get('/')
    assert 'My Customer's Homepage' in response.content.decode()

What will be in the page depends on the template, obviously, but you get the idea.

As you can see, we happen to use pytest-django, but content creation is the same for any Django test framework.

Exporting existing content as fixtures

(This section was added on 2018-04-30.)

To export existing content as fixtures, to easily create test content, use Django’s dumpdata command.

By default dumpdata will store content_type as a number (the pk). This is not safe, as it’s not guaranteed contenttypes will have the same pk across installations.

If you use dumpdata's --natural-foreign switch, the content_type will be set as ["app", "model"] as above.

A pull request to improve’s Wagtail documentation with this has been submitted.

We love code