对 Wagtail站点进行测试

Wagtail自带了一些可以简化编写站点测试的工具。

WagtailPageTests

  • class wagtail.tests.utils.WagtailPageTests

WagtailPageTestsdjango.test.TestCase 进行了扩展,加入了一些新的 assert 方法。可对此类进行扩展,以利用其各个方法:

  1. from wagtail.tests.utils import WagtailPageTests
  2. from myapp.models import MyPage
  3. class MyPageTests(WagtailPageTests):
  4. def test_can_create_a_page(self):
  5. ...
  • assertCanCreatAt(parent_model, child_model, msg=None)

断言某个特定子页面类型可在某个父页面类型下进行创建。parent_modelchild_model 应是要测试的页面类型。

  1. def test_can_create_under_home_page(sefl):
  2. # 这里可在某个 HomePage 下创建一个 ContentPage
  3. self.assertCanCreateAt(HomePage, ContentPage)
  • assertCanNotCreateAt(parent_model, child_model, msg=None)

断言不能在某个父页面类型下创建某个特定的子页面类型。parent_modelchild_model应是要测试的页面类。

  1. def test_cant_create_under_event_page(self):
  2. # 这里无法在 EventPage 下创建 ContentPage 的页面
  3. self.assertCanNotCreateAt(EventPage, ContentPage)
  • assertCanCreate(parent, child_model, data, msg=None)

断言某个给定页面类型的子页面,可在该父页面下,使用提供的 POST 数据进行创建。

parent 应是某个页面实例,同时child_model应是一个页面子类。data应是一个将在Wagtail管理界面的页面创建方法处将要 POST 提交的字典。

  1. from wagtail.tests.utils.form_data import netsted_form_data, streamfield
  2. def test_can_create_content_page(self):
  3. # 获取主页
  4. root_page = HomePage.objects.get(pk=2)
  5. # 断言可在这里以该 POST 数据,制作一个 ContentPage
  6. self.assertCanCreate(root_page, ContentPage, nested_form_data({
  7. 'title': '关于我们',
  8. 'body': streamfield([
  9. ('text', 'Lorem ipsum dolor sit amet'),
  10. ]),
  11. }))

请参阅 表单数据助手 了解有关构造 POST 数据的一些有用函数。

  • assertAllowedParentPageTypes(child_model, parent_models, msg=None)

对只能在parent_models 创建 child_model 这一事实进行测试。

在父模型已经设置了 Page.subpage_types时, 所允许的父模型清单,可与Page.parent_page 中设置的不同。

  1. def test_content_page_parent_pages(self):
  2. # ContentPage 只能在 HomePage 或另一个 ContentPage 下创建
  3. self.assertAllowedParentPageTypes(ContentPage, {HomePage, ContentPage})
  4. # EventPage 只能在某个 EventIndex 下被创建
  5. self.assertAllowedParentPageTypes(EventPage, {EventIndex})
  • assertAllowedSubpageTypes(parent_model, child_models, msg=None)

parent_model下只能创建的页面类型为 child_models 这一事实进行测试。

在子模型已被设置为Page.parent_page_types时,所允许的子模型清单可与 Page.subpage_types 中设置的有所不同。

  1. def test_content_page_subpages(self):
  2. # ContentPage 只能有其他的 ContentPage 子页面
  3. self.assertAllowedSubPageTypes(ContentPage, {ContentPage})
  4. # HomePage 只能有 ContentPage 与 EventIndex 的子页面
  5. self.assertAllowedParentPageTypes(HomePage, {ContentPage, EventIndex})

表单数据助手

assertCanCreate方法,要求有与页面编辑表单中所提交数据同样格式的页面数据传递给他。对于复杂的页面类型,就难以手动构造该数据结构;模块wagtail.tests.utils.form_data就提供了一套助手函数,用于帮助完成 POST 数据的构造。

  • wagtail.tests.utils.form_data.nested_form_data(data)

将某个嵌套字典的数据结构,转换为带有连字符分隔键的平面表单数据字典(a flat form data dict with hyphen-separated keys)。

  1. nested_form_data({
  2. 'foo': 'bar',
  3. 'parent': {
  4. 'child': 'field',
  5. },
  6. })
  7. # 将返回:{'foo': 'bar', 'parent-child': 'field'}
  • wagtail.tests.utils.form_data.rich_text(value, editor='default', features=None)

将一个类似于 HTML 的富文本字符串,转换为当前活动的富文本编辑器要要求的数据格式。

参数

  1. self.assetCanCreate(root_page, ContentPage, nested_form_data({
  2. 'title': '关于我们',
  3. 'body': rich_text('<p>Lorem ipsum dolor sit amet</p>'),
  4. }))
  • wagtail.tests.utils.form_data.streafield(items)

取得一个元组的清单(block_tyepvalue),并将其转换为 StreamField的表单数据。在某个 nested_form_data() 调用中使用该方法,将字段名称作为键。

  1. nested_form_data({'content': streamfield([
  2. ('text', 'Hello, world'),
  3. ])})
  4. # 将返回:
  5. # {
  6. # 'content-count': '1',
  7. # 'content-0-type': 'text',
  8. # 'content-0-value': 'Hello, world',
  9. # 'content-0-order': '0',
  10. # 'content-0-deleted': '',
  11. # }
  • wagtail.tests.utils.form_data.inline_formset(items, initial=0, min=0, max=1000)

取得一个 InlineFormset 的表单数据清单,并将其转换为有效的POST数据。在某个 nested_form_data() 调用中使用此方法,以表单集的关系名称作为键(with the formset relation name as the key)。

  1. nested_form_data({'lines': inline_formset([
  2. {'text': 'Hello'},
  3. {'text': 'World'},
  4. ])})
  5. # 将返回:
  6. # {
  7. # 'lines-TOTAL_FORMS': '2',
  8. # 'lines-INITIAL_FORMS': '0',
  9. # 'lines-MIN_NUM_FORMS': '0',
  10. # 'lines-MAX_NUM_FORMS': '1000',
  11. # 'lines-0-text': 'Hello',
  12. # 'lines-0-ORDER': '0',
  13. # 'lines-0-DELETE': '',
  14. # 'lines-1-text': 'World',
  15. # 'lines-1-ORDER': '1',
  16. # 'lines-1-DELETE': '',
  17. # }

Fixtures

使用 dumpdata

通过创建出一些开发环境中的内容,并使用Django 的 dumpdata 命令,是为测试目的而创建 一些 fixtures的最佳方法。

请注意默认的 dumpdata 将以主键来表示 content_type;在添加/移除模型时,这可能会导致一致性问题,因为内容类型是独立于 fixtures 而单独生成的。为防止这个问题,就要使用 --natural-foreign 命令开关,此命令开关将使用 ["app", "model"] 来表示内容类型。

手动修改

可手动修改复制的 fixtures,或设置完全手动地编写这些 fixtures。以下是一些需要留意的地方。

定制页面模型

在创建 fixtures 中的定制页面模型时,将需要同时添加一个 wagtailcore.page 的条目,以及一个定制页面模型的条目。

假设有着一个定义了Homepage(Page) 类的 website 模块。那么就可以在某个 fixture 中创建以下的一个主页:

  1. [
  2. {
  3. "model": "wagtailcore.page",
  4. "pk": 3,
  5. "fileds": {
  6. "title": "客户的主页",
  7. "content_type": ["website", "homepage"],
  8. "depth": 2
  9. }
  10. },
  11. {
  12. "model": "website.homepage",
  13. "pk": 3,
  14. "fields": {}
  15. }
  16. ]

Treebeard 字段

为了令到像是 get_parent 这类页面树的操作正确工作,因此填入 path / numchild / depth 字段是必要的。在某些不同寻常的情况下,若未给出值,url_path是另一个可导致错误的字段。

Treebeard 文档 可帮助理解其工作原理。