测试概述

Gutenberg同时包含PHP和JavaScript代码,并鼓励对两者进行测试和代码风格的linting。

为什么要测试?

除了测试给您的生活带来的乐趣外,测试也很重要,因为它们不仅有助于确保我们的应用程序按其应有的方式运行,而且还提供了如何使用代码的简明示例。

测试也是我们代码库的一部分,这意味着我们对其应用的标准与我们对所有应用程序代码应用的标准相同。

与所有代码一样,必须维护测试。为了进行测试而编写测试并不是我们的目标,相反,我们应该在覆盖预期和意外行为、快速执行和代码维护之间取得适当的平衡。

编写测试时,请考虑以下几点:

  • 我们正在测试哪些行为?
  • 当我们运行此代码时,可能会发生什么错误?
  • 测试是否测试了我们认为测试的内容?还是我们引入了假阳性/假阴性?
  • 它可读吗?其他贡献者能够通过查看代码的相应测试来了解代码的行为吗?

JavaScript测试

JavaScript使用测试杰斯特作为测试运行程序及其API全局变量(描述,测试,before每个等等)断言,模拟,间谍模拟函数。如果需要,您也可以使用反应测试库用于React组件测试。

应该注意的是,在过去,React组件是用但是,反应测试库(RTL)现在用于所有现有和新测试。

假设你已经遵循了说明书要安装节点和项目依赖项,可以使用NPM从命令行运行测试:

npm测试

Linting是用于执行编码标准和避免潜在错误的静态代码分析。此项目使用ESLint公司TypeScript的JavaScript类型检查捕捉这些问题。而上述npm测试将执行单元测试和代码linting,可以通过运行npm运行皮棉。一些JavaScript问题可以通过运行npm运行lint:js:fix.

为了改进开发人员的工作流程,您应该设置一个编辑器linting集成。请参阅入门文档了解更多信息。

要只运行单元测试而不运行linter,请使用npm运行测试:单元而不是。

文件夹结构

将测试保存在测试文件夹。测试文件应与测试主题文件同名。

+--测试|+--棒.js+--巴.js

只有测试文件(至少有一个测试用例)应该直接位于/测试。如果需要添加外部模拟或装置,请将其放置在子文件夹中,例如:

  • test/mocks/[文件名].js
  • test/fixtures/[文件名].js

导入测试

给定前面的文件夹结构,在导入您正在测试的代码,而不是使用项目路径。

很好

从“..”导入{bar}/巴';

不太好

从“components/foo/bar”导入{bar};

如果您决定将代码重新定位到应用程序目录中的另一个位置,这将使您的生活更加轻松。

描述测试

使用描述块对测试用例进行分组。每个测试用例最好只描述一种行为。

在测试用例中,尝试用简单的语言描述预期的行为。对于UI组件,这可能需要从用户角度描述预期行为,而不是解释代码内部。

很好

description('带标签的复选框',()=>{test('选中复选框应禁用表单提交按钮',()=>{...} );} );

不太好

description('带标签的复选框',()=>{test('选中复选框应将this.state.disableButton设置为“true”,()=>{...} );} );

设置和拆卸方法

Jest API包含一些漂亮的设置和拆卸方法允许您执行任务之前之后每个或所有测试,或特定描述块。

这些方法可以处理异步代码,以允许进行通常无法内联的设置。单个测试用例,您可以返回Promise,Jest将等待其解决:

//所有测试的一次性设置beforeAll(()=>someAsyncAction().then((resp)=>{window.someGlobal=resp;} ));//所有测试的一次性拆卸afterAll(()=>{window.someGlobal=空;} );

在每个之后在所有之后例如,通过重置状态数据,为测试后的“清理”提供一种完美(首选)的方式。

避免在断言之后放置清理代码,因为如果这些测试中的任何一个失败,清理都不会发生,并且可能会导致不相关的测试失败。

嘲笑依赖关系

依赖注入

将依赖项作为参数传递给函数通常可以使代码更容易测试。如果可能,请避免在更高范围内引用依赖项。

不太好

从'导入VALID_VALUES_LIST/常数';函数isValueValid(value){return VALID_VALUES_LIST.includes(值);}

在这里,我们必须从中导入并使用一个值有效值列表为了通过:

期望(isValueValid(VALID_VALUES_LIST[0]).toBe(true);

上述断言测试了两种行为:1)函数可以检测列表中的项,以及2)它可以检测中的项有效值列表.

但如果我们不关心存储在有效值列表,或者如果列表是通过HTTP请求获取的,我们只想测试isValue有效可以检测列表中的项目吗?

很好

函数isValueValid(value,validValuesList=[]){return validValuesList.includes(value);}

因为我们将列表作为参数传递,所以可以传递mock有效值列表我们的测试中的值,另外,还要测试几个场景:

expect(isValueValid('hulk',['batman','superman'])).toBe(false);

expect(isValueValid('hulk',null)).toBe(false);

expect(isValueValid('hulk',[])).toBe(false);

expect(isValueValid('hulk',['iron man','hulk'])).toBe(true);

导入的依赖项

通常,我们的代码会在多个位置使用导入的外部和内部库中的方法和属性,这使得传递参数变得混乱且不切实际。对于这些情况笑话模仿提供了一种简洁的方法来存根这些依赖项。

例如,假设我们配置模块通过功能标志控制大量功能。

//比尔博.js从“config”导入config;导出常量isBilboVisible=()=>config.isEnabled(“环”)?false:真;

为了测试每个条件下的行为,我们存根config对象并使用jest mocking函数控制已启用.

//测试/bilbo.js从“config”导入{isEnabled};从“..”导入{isBilboVisible}/比尔博';jest.mock('config',()=>({//bilbo默认可见isEnabled:jest.fn(()=>false),} ) );description(“bilbo模块”,()=>{test('默认情况下bilbo应可见',()=>{expect(isBilboVisible()).toBe(true);} );test('启用'the-ring'配置功能标志时,bilbo应该不可见',()=>{isEnabled.mockImplementationOnce((name)=>name===“the-ring”);expect(isBilboVisible()).toBe(false);} );} );

测试全球

我们可以使用笑话间谍以测试调用全局方法的代码。

从“..”导入{myModuleFunctionThatOpensNewWindow}/my-module';description(“我的模块”,()=>{beforeAll(()=>{jest.spyOn(global,'open').mockImplementation(()=>true);} );测试(“某物”,()=>{myModule函数打开新窗口();expect(global.open).toHaveBeenCalled();} );} );

用户交互

模拟用户交互是从用户的角度编写测试,因此避免测试实现细节。

使用Testing Library编写测试时,有两种主要的模拟用户交互的方法:

  1. 这个fireEvent(火灾事件)API,一个用于触发测试库核心API的DOM事件部分的实用程序。
  2. 这个用户事件library是Testing library的配套库,它通过分派在浏览器中发生交互时发生的事件来模拟用户交互。

内置火灾事件是用于调度DOM事件的实用程序。它准确地发送测试规范中描述的事件,即使这些准确的事件从未在浏览器中的实际交互中发送。

另一方面用户事件库公开更高级别的方法(例如。类型,选择选项,清楚的,双击…),发送事件,就像用户与文档交互时发生的事件一样,并处理任何特定于反应的异常。

基于上述原因,这个用户事件在编写用户交互测试时,建议使用库.

不太好:使用fireEvent(火灾事件)调度DOM事件。

从“@testing-library/react”导入{render,screen};test('键入新值时激发onChange',()=>{const spyOnChange=jest.fn();//具有一个“输入”和一个“选择”的组件。render(<MyComponent onChange={spyOnChange}/>);const输入=screen.getByRole(“文本框”);input.focus();//没有点击,没有关键事件。fireEvent.change(输入,{target:{value:62}});//用“62”作为参数调用一次“onChange”回调。期望(spyOnChange).toHaveBeenCalledTimes(1);const select=screen.getByRole(“列表框”);select.focus();//未调度指针事件。fireEvent.change(选择,{target:{value:'optionValue'}});// ...

很好:使用用户事件以模拟用户事件。

从“@testing-library/react”导入{render,screen};从“@testing-library/user-event”导入userEvent;test('键入新值时激发onChange',async()=>{const user=userEvent.setup();const spyOnChange=jest.fn();//具有一个“输入”和一个“选择”的组件。呈现(<MyComponent onChange={spyOnChange}/>);const input=screen.getByRole(“文本框”);//聚焦元素,选择并删除其所有内容。等待user.clear(输入);//单击元素,分别键入每个字符(生成按键,//按键和按键向上事件)。await-user.type(输入,'62');//使用以下参数调用“onChange”回调3次://-1:清除(“”)// - 2: '6'// - 3: '62'期望(spyOnChange).toHaveBeenCalledTimes(3);const select=screen.getByRole(“列表框”);//调度焦点、指针、鼠标、单击和更改的事件。等待用户。选择选项(select,['optionValue']);// ...} );

块UI的集成测试

集成测试被定义为一种测试类型,其中不同部分作为一个组进行测试。在这种情况下,我们想要测试的部分是特定块或编辑器逻辑所需呈现的不同组件。最后,它们与单元测试非常相似,因为它们使用Jest库使用相同的命令运行。主要区别在于,对于集成测试,块在块编辑器的特殊实例.

这种方法的优点是可以测试块编辑器的大部分功能(块工具栏和检查器面板交互等),而无需启动完整的e2e测试框架。这意味着测试可以更快、更可靠地运行。建议集成测试尽可能多地涵盖块的UI功能,e2e测试用于需要完整浏览器环境的交互,例如文件上传、拖放等。

盖块是一个块的示例,该块使用此级别的测试来覆盖大部分编辑器交互。

要为集成测试设置jest文件,请执行以下操作:

从“test/integration/helpers/integration-test-editor”导入{initializeEditor};异步函数设置(属性){const testBlock={名称:'core/cover',属性};return initializeEditor(testBlock);}

这个initializeEditor(初始化编辑器)函数返回@测试-布雷里/反应 提供方法。它还将接受一组块元数据对象,允许您使用多个块设置编辑器。

集成测试编辑器模块还导出select块可用于选择块包装上的aria-label要测试的块,例如“块:封面”。

快照测试

这是一个概述快照测试以及如何最好地利用快照测试。

TL;DR损坏的快照

当快照测试失败时,这仅仅意味着组件的呈现已更改。如果这是无意的,那么快照测试只是防止了一个错误😊

但是,如果更改是有意的,请按照以下步骤更新快照。运行以下命令更新快照:

#--testPathPattern是可选的,但只运行匹配测试会更快npm运行测试:单元----updateSnapshot--testPathPattern路径/to/tests#更新e2e测试的快照npm运行测试:e2e---更新快照路径/to/spec
  1. 审查差异,确保更改是预期的和有意的。
  2. 提交。

什么是快照?

快照只是测试生成的一些数据结构的表示。快照存储在文件中,并与测试一起提交。运行测试时,将生成的数据结构与文件上的快照进行比较。

制作快照非常容易:

test(“foobar测试”,()=>{const foobar={foo:“bar”};expect(foobar).toMatchSnapshot();} );

这是生成的快照:

exports[`test foobar test 1`]=`对象{“foo”:“bar”,}`;

永远不要直接创建或修改快照,快照是由测试生成和更新的。

优势

  • 简单明了地添加测试。
  • 防止意外更改。
  • 操作简单。
  • 在不运行应用程序的情况下显示内部结构。

缺点

  • 没有表现力。
  • 仅在引入更改时捕获问题。
  • 对于任何不确定性都是有问题的。

用例

快照主要针对组件测试。它们使我们意识到组件结构的变化理想的如果快照在一系列提交过程中保持最新,快照差异会记录组件结构的演变。非常酷😎

从“@测试库/rect”导入{render,screen};从“solar-system”导入SolarSystem;描述(“SolarSystem”,()=>{test(“应该呈现”,()=>{const{container}=渲染(<SolarSystem/>);expect(container).toMatchSnapshot();} );test('如果行星为真,则应包含火星',()=>{const{container}=渲染(<SolarSystem planets/>);expect(container).toMatchSnapshot();expect(screen.getByText(/mars/i)).toBeInTheDocument();} );} );

Reducer测试也非常适合快照。它们通常是大型、复杂的数据结构,不应意外更改,正是快照所擅长的!

使用快照

当快照不匹配时,CI测试失败可能会让您大吃一惊。你需要更新快照如果预期会发生变化。快速而肮脏的解决方案是用--更新快照可按以下方式进行:

npm运行测试:单元----updateSnapshot--testPathPattern路径/to/tests

--测试路径模式不是必需的,但指定路径将通过运行测试子集加快速度。

这是个好主意npm运行测试:单元:监视在后台运行。Jest将只运行更改文件的相关测试,当快照测试失败时,只需点击u个更新快照!

痛点

非确定性测试可能无法生成一致的快照,因此要小心。当处理任何随机的、基于时间的或其他不确定性的内容时,快照都会有问题。

连接的组件很难使用。要对连接的组件进行快照,您可能需要导出未连接的组件:

//我的组件.js导出{MyComponent};导出默认连接(mapStateToProps)(MyComponent);//测试/my-component.js从“..”导入{MyComponent};//运行那些MyComponent测试…

需要手动提供连接的道具。这是一个审核连接状态的好机会。

最佳实践

如果您正在启动重构,快照非常好,您可以将其添加为分支上的第一个提交,并观察它们的发展。

快照本身并不能表达我们的期望。快照最好与描述我们期望的其他测试结合使用,如上例所示:

test('如果行星为真,则应包含火星',()=>{const{container}=渲染(<SolarSystem planets/>);//快照将捕获意外更改expect(container).toMatchSnapshot();//这就是我们实际期望在测试中发现的expect(screen.getByText(/mars/i)).toBeInTheDocument();} );

另一个好方法是使用匹配差异快照功能(由快照差异包裹),它只允许快照DOM的两个不同状态之间的差异。此方法有助于测试属性更改对生成的DOM的影响,同时生成更小的快照,如以下示例所示:

test('当isShady为true时,应呈现较暗的背景',()=>{const{container}=呈现(<CardBody>Body</CardBody>);const{container:containerShady}=呈现(<CardBody isShady>正文);expect(container).toMatchDiffSnapshot(containerShady);} );

类似地匹配样式差异快照函数只允许快照样式与组件的两种不同状态关联,如本例所示:

test('应该呈现边距',()=>{const{container:spacer}=render(<spacer/>);const{container:spacerWithMargin}=呈现(<Spacer margin={5}/>);expect(spacerWithMargin).toMatchStyleDiffSnapshot(spacer);} );

故障排除

有时,我们需要为一些使用参考文献的故事模仿参考文献。查看以下文档以了解更多信息:

在这种情况下,您可能会看到测试失败和类型错误Jest在尝试从访问属性的行中报告参考电流.

调试Jest单元测试

正在运行npm运行测试:单元:调试将在调试模式下启动测试,因此节点检查器客户端可以连接到进程并检查执行情况。有关使用Google Chrome或Visual Studio代码作为检查器客户端的说明,请参见wp-scripts文档.

本机移动测试

单元测试套件的一部分是在React native中开发的一组Jest测试运行练习本机-移动代码路径。由于这些测试在Node上运行,因此可以在开发机器上本地启动,而无需特定的本地Android或iOS开发工具或SDK。这还意味着可以使用典型的开发工具对它们进行调试。继续阅读有关如何调试的说明。

调试本机移动单元测试

要在调试模式下本地运行测试,请执行以下步骤:

  1. 确保您已跑步npm-ci公司安装所有软件包
  2. 运行npm运行测试:本机:调试位于CLI上的Gutenberg根文件夹中。节点现在正在等待调试器连接。
  3. 正常开放铬://检查在Chrome中
  4. 在“远程目标”部分下,查找../../节点模块/.bin/jest目标并单击“inspect”链接。这将打开一个新窗口,其中Chrome DevTools调试器附加到进程,并在笑话.js文件。或者,如果目标不可见,请单击为节点打开专用DevTools链接。
  5. 您可以放置断点或调试器;语句贯穿整个代码,包括测试代码,以停止和检查
  6. 单击“播放”按钮恢复执行
  7. 享受本机移动单元测试的调试!

本机移动端到端测试

Gutenberg的贡献者将注意到,PR包括在Android和iOS上运行本地移动E2E测试的持续集成E2E测试。有关失败测试的故障排除,请参阅我们的指南持续集成中的本地移动测试。有关在本地运行这些测试的更多信息,请参阅在这里.

本机移动集成测试

目前正在努力使用反应测试库库。可以找到编写集成测试的指南在这里.

端到端测试

大多数现有的端到端测试当前使用木偶演员作为一名无头铬驱动程序进行测试包/e2e测试和仍然由杰斯特测试跑步者。

有一个正在进行的项目把他们从木偶师转移到剧作家那里。建议尽可能在Playwright中编写新的e2e测试。以下部分主要适用于旧的Jest+Puppeter框架。请参阅专用的指导如果你和剧作家一起写测试。

使用wp-env

如果您使用内置当地环境,您可以使用以下命令在本地运行e2e测试:

npm运行测试:e2e

或以交互方式

npm运行测试:e2e:监视

有时,在运行测试时观察浏览器很有用。然后,使用以下命令:

npm运行测试:e2e:watch--puppeter-interactive

您可以使用控制执行速度--木偶舞者:

npm运行测试:e2e:watch--puppeter-interactive--puppeer-slowmo=200

此外,还可以在浏览器中自动打开开发工具进行交互式调试:

npm运行测试:e2e:watch--puppeter-devtools

使用备用环境

如果使用的设置不同于wp-env公司,首先需要将e2e测试插件符号链接到测试站点,从站点的插件目录运行:

ln-s gutenberg/packages/e2e测试/插件/*。

然后要运行测试,请指定站点的基本URL、用户名和密码。例如,如果您的测试站点位于`http://wp.test(测试)`,使用:

WP_BASE_URL=http://wp.test(测试)npm运行测试:e2e----wordpress-username=admin--wordpress-password=password

场景测试

如果您发现端到端测试在本地运行时通过,但在GitHub Actions中失败,则可以通过模拟缓慢的CPU或网络来隔离CPU或网络绑定竞争条件:

THROTTLE_CPU=4 npm运行测试:e2e

节气门_CPU是减速系数(在本例中为4x减速乘数)

请参见Chrome文档:setCPUThrottlingRate

SLOW_NETWORK=真npm运行测试:e2e

慢速网络在Chrome开发工具中模拟相当于“Fast 3G”的网络速度。

请参见Chrome文档:emulateNetworkConditions网络管理器.js

离线=真正的npm运行测试:e2e

离线模拟网络断开连接。

请参见Chrome文档:模拟网络条件

芯块测试

每个核心块都需要至少有一组fixture文件用于其主save函数,每个deprecation都需要一个文件集。这些fixture测试块的解析和序列化。请参见集成测试夹具自述文件获取更多信息和说明。

古怪的测试

测试被视为薄片状的当它可以在不更改任何代码的情况下通过或失败多次重试时。我们最多自动重试失败的测试两次在CI上自动检测并向GitHub报告问题[类型]薄片试验标签通过报告缺陷试验GitHub操作。请注意,连续三次失败的测试不会被视为片状测试,也不会向问题报告。

PHP测试

PHP使用测试PHP装置作为测试框架。如果您使用内置当地环境,可以使用以下命令在本地运行PHP测试:

npm运行测试:php

要在文件更改时自动重新运行测试(类似于Jest),请运行:

npm运行测试:php:watch

注意:phpunit命令需要wp-env公司要运行和要安装的编写器依赖项。如果包脚本尚未运行,它将为您启动wp-env。

在其他环境中,运行作曲家运行测试作曲家运行测试:观看.

PHP中的代码样式使用PHP_代码嗅探器。建议您安装PHP_CodeSniffer和用于PHP_CodeSniffer的WordPress编码标准规则集使用作曲家。安装Composer后,运行编写器安装从项目目录安装依赖项。以上内容npm运行测试:php将执行单元测试和代码linting。代码linting可以通过运行独立验证npm运行lint:php.

要只运行单元测试而不运行linter,请使用npm运行测试:单位:php而不是。

性能测试

为了确保编辑器在我们添加功能时保持性能,我们监控拉动请求和发布对一些关键指标的影响,包括:

  • 加载编辑器所需的时间。
  • 输入时浏览器响应所需的时间。
  • 选择块所需的时间。

性能测试是运行编辑器并捕获这些度量的端到端测试。确保您已经准备好了e2e测试环境。

要设置e2e测试环境,请签出Gutenberg存储库并切换到要测试的分支。运行以下命令以准备环境。

nvm使用和npm安装npm运行构建:包

要运行测试,请运行以下命令:

npm运行测试:性能

这将为您提供运行环境中当前分支/代码的结果。

除此之外,您还可以通过运行以下命令来比较分支(或标记或提交)之间的度量./bin/plugin/cli.js性能[分支],例如:

./bin/plugin/cli.js perf-trunk v8.1.0 v8.0.0

最后,您可以通过另一个--测试分支参数指定要运行的分支的性能测试文件。这在修改/扩展性能测试时特别有用:

./bin/plugin/cli.js perf-trunk v8.1.0 v8.0.0--tests-branch添加/perf-tests-coverage

注释此命令可能需要一些时间来执行基准测试。在运行时,请确保避免使用计算机或进行大量后台处理,以最小化可能影响分支结果的外部因素。