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.