Drupal 10:如何使用默认内容部署来创建测试内容

Drupal 10:使用默认内容部署创建测试内容

2023年7月9日 - 阅读时长37分钟

在Drupal项目中进行行为测试,能够确保所有功能都按预期运行。这在进行Drupal开发Drupal升级时尤为关键,因为代码更新可能会引入难以察觉或查找耗时的错误。

行为测试的一个重要方面是网站内容,这对于许多Drupal网站的正常运行通常是必不可少的。许多Drupal网站会使用分类术语,这些术语在视图中以各种方式过滤内容。

还有一些结构页面充当网站其他部分的路标,它们在导航中通常十分重要。虽然可以直接访问这些页面,但测试用户的端到端旅程往往很有用,这涉及导航到正在测试的功能。

确保网站包含内容的一种方法是将生产数据库复制到测试环境中。不过,使用生产数据库进行测试存在几个缺点,其中最主要的是复制数据的复杂性。一些生产数据库非常庞大,将其导入测试环境可能会使测试耗时数小时。

最大的问题是要确保开发网站不包含任何个人信息,因为这涉及安全问题,甚至可能导致向用户发送测试邮件等问题。虽然有解决办法,但团队通常需要花费大量时间,确保测试环境中没有静态的个人身份信息。

更好的办法是使用默认内容创建Drupal网站,以便在运行测试之前,网站处于已知状态。这意味着网站的功能将与生产网站相同,只是没有所有的个人信息。这种方法的理念是为测试创建一个已知的测试环境。

这里采用了名为安排(Arrange)行动(Act)断言(Assert)(也称为AAA)的方法,即把网站设置为已知状态,对该内容执行操作,然后进行测试,以确保存在正确的功能。

为了让这个系统在Drupal中运行,我公司使用了默认内容部署(Default Content Deploy)模块,该模块允许将内容导出和导入到网站中。此模块会将内容导出为一系列JSON文件,然后可以在需要时导入(或重新导入)。在Drupal模块开发过程中,这类模块能发挥重要作用。

在本文中,我公司将介绍如何开始使用默认内容部署模块,以及如何运用该模块的工作流程简化工作。

一、安装和配置默认内容部署模块

要安装该模块,首先需要使用Composer将其添加到网站的源代码中。

composer require --dev drupal/default_content_deploy

--dev标志用于确保该模块不会部署到生产环境中。

然后可以使用以下Drush命令激活该模块。

drush en default_content_deploy

安装完成后,只需在settings.php文件中添加一个配置设置,让模块知道将内容导出到何处。以下设置将内容导出目录设置为Drupal网站根目录之外名为“content”的目录,但如果需要,也可以将其设置为绝对路径。

$settings['default_content_deploy_content_directory'] = '../content';

可以通过运行Drush并查看以下输出来检查模块是否正确安装。

default-content-deploy:
  default-content-deploy:entity-list (dcd-entity-list)             列出当前内容实体类型。
  default-content-deploy:export (dcde)                             导出单个实体或一组实体。
  default-content-deploy:export-site (dcdes)                       导出整个网站内容。
  default-content-deploy:export-with-references (dcder)            导出带有引用的单个实体。
  default-content-deploy:import (dcdi)                             导入内容目录中定义的所有内容。
  default-content-deploy:uuid-info (dcd-uuid-info, dcd-uuid)       获取 UUID实体的信息。

现在就可以开始导出内容了。

二、使用默认内容部署模块导出内容

要导出内容,需要知道要导出什么。可以使用default-content-deploy:entity-list Drush命令。

drush default-content-deploy:entity-list

这将打印出所有可以导出的内容列表,在标准Drupal安装的情况下,列表如下。

block_content(自定义块)
comment(评论)
contact_message(联系信息)
file(文件)
menu_link_content(自定义菜单链接)
node(内容)
paragraph(段落)
path_alias(URL别名)
shortcut(快捷链接)
taxonomy_term(分类术语)
user(用户)

可以使用default-content-deploy:export-site Drush命令导出整个网站。

drush default-content-deploy:export-site

此操作将在drupal/content目录中创建一堆JSON文件,这是之前设置的导出目标位置。

然而,导出整个网站通常会引发问题,因为会导出网站上具有内容的每种类型的实体。这包括菜单项和路径别名等项目,这些项目在导出中不需要,因为它们是在创建内容时自动生成的。还会导出默认的匿名用户和管理用户,这些用户是在安装网站时生成的。这可能会在尝试导入内容或稍后更新内容导出时导致问题。

更好的方案是选择要导出的内容。最好先选择要导出的内容页面,然后再从那里扩展。例如,可以使用default-content-deploy:export命令并传递“node”类型来导出网站中的所有节点:

drush default-content-deploy:export node

也可以提供参数来单独选择特定页面或页面类型,作为单个项目或逗号分隔的列表。

以下是该命令的一些实际示例。

# 导出第1页。
drush default-content-deploy:export node --entity_id=1

# 导出第1页和第2页。
drush default-content-deploy:export node --entity_id=1,2

# 仅导出“page”类型的节点。
drush default-content-deploy:export node --bundle=page

# 导出“page”和“article”类型的节点。
drush default-content-deploy:export node --bundle=page,article

# 导出除第3页之外的所有节点。
drush default-content-deploy:export node --skip_entities=3

默认内容部署模块不会导出引用的实体,因此一旦导出了页面,就需要导出与该实体相关的所有内容。例如,以下实体类型通常与节点相关,在导出所需的节点后应导出这些内容。

导出所有分类术语。

drush default-content-deploy:export taxonomy_term

导出所有媒体项目。

drush default-content-deploy:export media

导出所有文件。

drush default-content-deploy:export file

导出所有段落。

drush default-content-deploy:export paragraph

为了在再次导入时生成完整的网站,必须导出所需的所有内容,这可能涉及找出要导出的不同实体的ID。默认内容部署模块确实有一个名为default-content-deploy:export-with-references的Drush命令,该命令应该可以简化这个过程,但我公司还未能使其正常工作。

关于导出内容的一些注意事项。

  • 最好完全重新安装网站,然后导出所需的内容更改。这比尝试从包含其他内容项的网站导出内容更可取。
  • 默认内容不会导出词汇表,因为它们是配置系统的一部分。仍然需要导出任何希望可用的术语。
  • 智能日期(Smart Date)模块字段在导出时存在一些问题,因此需要修复创建的任何事件捆绑包导出。(见下面的常见问题)

三、导入内容

要从内容目录导入所有内容,请运行以下Drush命令。

drush default-content-deploy:import --force-override --yes

此命令将告知创建的实体数量,并在完成时报告成功。force-override选项将强制更新任何已导入的内容为导出的版本。如果这是第一次安装内容,那么可以根据需要省略此参数。

可以添加详细输出标志以打印出有关导入内容的更多信息。

四、将文件和媒体导出到测试内容中

默认文件由“更好的规范化器(Better Normalizers)”模块处理,可以使用以下require命令将此模块添加到代码库中。

composer require --dev drupal/better_normalizers

然后使用以下drush命令启用它。

drush en better_normalizers

完成上述操作后,现在可以毫无问题地导出文件实体。文件本身将以base64编码的形式包含在内容JSON中。

请注意,此模块还要求在composer.json文件中将“minimum-stability”设置为“beta”。目前还没有Drupal 10版本,但正在努力解决这个问题。

五、默认内容部署工作流程

我们已经分别了解了导入和导出过程,但是如何将这些过程结合起来,以便在创建新的测试内容时取得最大的成功呢?

以下步骤将确保在使用此系统时获得最大的成功。

1) 重新安装网站,然后启用default_content_deploybetter_normalizers模块。由于内容项与其系统中的ID号之间的关系,最好从头开始,以避免对要导出的内容项产生混淆。

2) 导入所有内容,以确保网站中的所有内容都是“默认”的。

3) 进行所需的内容更改,同时确保记录所添加的内容类型。

4) 通过专门针对更新的内容实体来导出内容更改。默认内容部署模块不会“深入”到内容中导出引用,因此需要确保导出任何希望作为默认内容导入的链接实体。这基本上意味着需要按顺序导出文件、导出媒体项目、导出段落,然后导出节点。通常,一次仅导出一种类型的实体,而不是选择段落ID或捆绑包类型,可以获得最佳效果。

5) 测试导入!导出所有内容后,重新安装网站并测试导入过程。

6) 确保测试套件能够运行。导出内容是为了针对该内容生成测试,因此需要确保测试套件能够在对内容导出所做的更改下正常运行。

7) 编写新的测试。

8) 如果一切正常,将文件提交到git。

提示:

- 真的,一次不要更改太多内容。每次添加一个页面或一个段落,并确保所有内容都能协同工作。这些更改在git中也更容易被发现。
- 看到已导出的文件发生更改不要感到惊讶。在导入和导出内容时,ID号和其他辅助信息等可能会更新。这是正常的。
- 确保导出要更新的实体的两个部分。这意味着既要导出节点,也要导出引用的实体,如段落或分类术语。
- 由于默认内容用于测试,不要害怕导出多个将由测试处理的节点。例如,可能想在一个节点上测试评论,在另一个节点上测试发布工作流程。这可以使测试保持清晰,并防止它们相互依赖,从而变得脆弱。

六、常见问题

以下是使用内容部署模块时可能遇到的一些常见问题及其解决方案。

1. 导出所有内容会导致问题

虽然有从网站导出每一项内容的选项,但这样做通常会导致错误。这些错误主要是由与内容导出过程不兼容的模块引起的,因此在尝试导入它们时会导致错误。还会发现,安装网站时会生成相当多的内容项,因此这些项将与实际的测试内容一起导出。像菜单项、路径,甚至默认的匿名用户都会包含在导出内容中。

更好的方法是只导出测试所需的内容。

2. 导出评论后无法导入

使用此模块导出评论很容易出错。这不是模块本身的问题,更多的是要确保导入过程能够链接到正确的内容项和正确的用户,这通常会导致评论与不存在的用户之间的连接断开。

如果绝对需要在测试中使用评论,那么最好使用行为测试框架在默认内容上创建评论,然后执行所需的操作。我公司发现这样比尝试正确导入评论要容易得多。

3. 实体类型不支持UUID

当尝试导出不使用UUID的实体时会出现这种情况,如果创建了不使用该字段的自定义实体类型,就可能发生这种情况。还有一些第三方模块可能不支持UUID,因此默认内容部署模块也不支持它们。

在导出过程中很容易发现这个问题,因为它会在内容导出目录中创建名为“.json”的文件。

content/
-- my_custom_entity/
---- .json

解决此问题的方法是修改实体,使其支持UUID,或者使用其他机制(如安装配置文件)将它们导入到网站中。

4. 字段“value”未知

这是由某些字段类型无法正确导出为默认内容JSON格式引起的。事件内容项上的智能日期字段就是这样一个例子。

问题的原因是字段没有包含在字段定义中。相反,字段的内容直接转储到JSON的根目录。

例如,以下是从网站导出的一个事件节点:

"field_event_capacity": [
{
"value": 100
}
],
"value": "2030-01-01T09:00:00+00:00",
"end_value": "2030-01-01T10:00:00+00:00",
"duration": 60,
"rrule": null,
"rrule_index": null,
"timezone": "",
"format": "Y-m-d\\TH:i:sP"
"field_event_location": [
{
"value": "Online"
}
],

为了解决这个问题,需要将事件日期字段包含在字段定义中:

"field_event_capacity": [
{
"value": 100
}
],
"field_event_date": [
{
"value": "2030-01-01T09:00:00+00:00",
"end_value": "2030-01-01T10:00:00+00:00",
"duration": 60,
"rrule": null,
"rrule_index": null,
"timezone": "",
"format": "Y-m-d\\TH:i:sP"
}
],
"field_event_location": [
{
"value": "Online"
}
],

现在内容将正确导入。

七、将其添加到测试工作流程中

现在已经很好地掌握了默认内容,需要考虑测试工作流程。创建的默认内容虽然对本地开发很有用,但与一个不错的行为测试系统结合使用时才能真正发挥优势。所需要做的就是以正确的方式设置Drupal网站,以便进行所需的测试。具体如何操作取决于运行的系统,但可能需要从头开始,许多持续集成环境都是这种情况。

设置环境的方式将取决于持续集成环境中可用的资源,但需要访问某种数据库,并能够通过Web地址安装和使用Drupal。我公司发现使用Docker容器对此非常有帮助,因为它简化了环境设置的复杂性。只需将所有版本和复杂性问题转移到Docker容器设置中即可。

接下来是安装网站。这可以通过几个Drush命令完成。

# 安装网站。
drush si standard --existing-config --yes --account-name=admin --account-pass=admin
# 清除Drupal缓存。
drush cr
# 导入网站配置。
drush cim

此时还应该记住运行任何主题构建器,以便网站能够使用完整构建的主题运行。这一步取决于用于构建主题的系统,但可能需要以下命令。

cd web/themes/custom/my_site_theme
npm install
npm run build

完成这些后,可以安装导入内容所需的模块,即默认内容部署模块和更好的规范化器模块。在进行Drupal 11开发时,这些模块的合理运用也至关重要。

drush en default_content_deploy better_normalizers --yes

分别启用这些模块的原因是为了降低网站配置的复杂性。这些模块仅用于使用内容测试网站,因此仅在需要执行该任务时才会安装。因此,在测试开始前快速安装这些模块的辅助命令是一个简单的步骤,可以集成到任何所需的测试设置中。

安装模块后,可以使用以下命令导入默认内容。

drush default-content-deploy:import --force-override --yes

现在可以开始测试网站了。这意味着像普通用户一样在网站上导航和使用它,但在这种情况下,网站处于已知状态,因此测试更具可预测性。

在理想情况下,每次运行新测试时都应该完全重新安装Drupal,然而,这可能不太实际。折中的办法是在每次测试运行开始时重新导入内容。在确保测试运行的纯净环境与测试完成所需的时间之间需要找到一个平衡。

编写测试时需要小心,以免测试的功能在重新导入内容时无法重置。有些实体不受内容导入过程的影响,因此会保留其原始状态。这意味着在测试购物车、点赞、评论或其他辅助页面数据时,有时需要新的策略才能正确进行测试。为了独立执行某些测试,可能需要生成不同的用户或不同的内容页面。

作为这种设置的一个示例,让我们看看Cypress。如果使用Cypress运行行为测试,则可以在每次测试运行开始时运行default-content-deploy:import命令重新导入内容。这可以在Cypress中使用before()函数实现,该函数位于Cypress目录中的cypress/support/e2e.js文件中。

before(function () {
    // 每次运行测试时导入默认内容。
    cy.exec('./vendor/bin/drush default-content-deploy:import --force-override --yes',{ timeout: 10000 }).its('code').should('eq', 0)
});

此命令本质上是运行Drush命令导入配置,并确保返回状态为0,这意味着命令没有错误。请注意,这里的超时设置得相当高,这是为了给所有内容模型的导入和更新留出足够的时间,并为测试套件的未来扩展提供空间。

可以在测试套件中使用after()函数(即before()函数的相反操作),以便将网站恢复到已知状态。如果创建了可能会影响其他测试的辅助页面数据,这一点尤为重要。以下是一个可以添加到Cypress中的测试套件示例模板。

describe('一个示例测试套件', () => {

  after(function () {
    // 删除测试创建的任何数据。
  })

  it('通过测试', () => {
    // 运行测试。
  })
})

与其在Cypress中搜索并删除创建的内容,不如创建一个Drush命令来清除数据。

八、总结

默认内容部署模块是一个非常强大的模块,结合更好的规范化器模块,在测试内容时会更加有用。它也让项目中的开发人员的工作更轻松,因为他们不需要依赖生产数据库的副本就能开展工作。相反,他们可以直接导入默认内容,然后开始进行所需的工作,对于Drupal模块开发Drupal开发都有很大帮助。

不过,该模块也存在一些小问题。某些类型的实体不太适合导出,因此如果可能的话,应尽量避免。此外,并非所有模块都与默认内容部署所需的JSON格式兼容,因此可能需要花费时间解决这些问题。

应该注意内容中的依赖关系,因为虽然可以导出段落和节点,但在不导入它们的情况下,很难发现哪些实体是相互关联的。

不要让测试环境或开发人员单独运行Drush命令,而应该以某种方式将这些命令封装起来。可以通过Makefile或某种任务运行器来实现,但应避免在导入(或导出)内容时需要记住添加哪些标志。

使用这种技术编写测试时要小心,因为可能会很快犯一些测试错误,给团队带来麻烦。利用测试套件中的“before”和“after”操作,确保环境处于已知状态。查看我公司关于编写测试时要避免的7个常见错误的文章,以确保从长远来看不会给自己带来麻烦。