随着React和Vue等网络框架和库的广泛采用,网络应用的开发已经从每个页面只有一个HTML文件转变为拥有小型可重复使用的组件来渲染应用的特定部分。
同样,测试实践也从只有页面的端到端(E2E)测试扩展到有几个单元测试,同时单独测试前端组件。
如果你一直使用Playwright为你的应用程序实现E2E测试,你可能知道Playwright现在提供了单独测试前端组件的能力。
本教程将指导你使用Playwright最近发布的实验性组件测试功能,为你的Svelte、Vue和React组件编写单元测试的过程。
如果你想直接进入完成的测试案例,代码可在GitHub上的playwright-ct 分支的 [playwright-frontend-e2e-tests](https://github.com/vickywane/playwright-frontend-e2e-tests) 仓库中。
前提条件
本教程包含实践步骤,将指导你完成使用Playwright测试用React、Vue和Svelte构建的组件。你将在一个Yarn工作区克隆一个包含React、Vue和Svelte组件的模板项目。
要跟上这些步骤,你将需要以下东西:
- 在你的电脑上安装Node.js v16
- 在你的电脑上安装Git CLI
- 对React、Vue或Svelte框架的基本熟悉程度
什么是 Playwright?
Playwright是一个开源的测试工具,主要支持网络应用,并对移动应用提供额外的实验性支持。
Playwright为你提供了使用JavaScript或TypeScript、Java、.NET和Python编写的可执行脚本自动测试的能力,而不是手动测试你的应用程序的界面。
Playwright的组件测试功能介绍
实验性的Playwright Test组件测试功能允许你创建测试案例,在其中导入和测试你的前端组件,而不是以编程方式导航到整个页面。
以前,已经使用Playwright进行E2E测试的开发者可能仍然需要单元测试。这些开发者不得不采用其他测试库,如Jest来直接测试他们的组件。
Playwright在2022年5月发布v1.22.0版本后,取消了这个限制,增加了组件测试功能。
目前,组件测试功能只支持React、Vue和Svelte。Playwright提供了一个额外的包,包裹了 [@playwright/test](https://www.npmjs.com/package/@playwright/test)包,以提供其他方法,如mount ,用于安装单个组件。
当你考虑使用Playwright进行前端单元测试时,请记住,组件测试功能仍处于实验阶段。因此,未来可能会对API进行修改。
使用Playwright测试你的组件
为了让你专注于使用Playright,本教程使用一个现有的monorepo项目,其中包含较小的React、Vue和Svelte应用程序。
monorepo使用Yarn工作区进行管理,React、Vue和Svelte应用程序将渲染同一个页面,显示从Rick and Morty API获取的JSON数据,其中有电视节目Rick and Morty的信息。
要开始工作,启动你的终端,使用Git CLI从GitHub仓库克隆预建的前端项目:
>git clone https://github.com/vickywane/playwright-frontend-e2e-tests.git
使用下面的命令,将目录改为克隆的项目,安装项目的依赖关系,并运行前端应用程序。该项目使用 [concurrently](https://www.npmjs.com/package/concurrently) 包下,从一个终端运行React、Vue和Svelte应用程序:
# change directory
cd playwright-frontend-e2e-tests
# install dependencies
yarn install
# start all applications
yarn start:all
然后你应该看到类似下面的东西:

通过为monorepo使用Yarn工作空间,你只需要在根目录下安装一次项目依赖,Yarn会自动将它们链接到其他项目。了解更多关于Yarn工作空间和monorepos的信息。
该项目中的每个前端应用程序显示从Rick and Morty API中获取的20个字符,这些字符跨越主组件中导入的三个子组件。在接下来的章节中,你将为每个子组件编写组件测试。
设置好项目后,让我们通过使用Playwright在单个应用程序中设置和编写E2E测试的过程。
为React组件设置Playwright组件测试
在开始编写E2E测试之前,使用你的浏览器导航到 http://localhost:3000,查看React应用程序。你将为下面图片中突出显示的SearchField 组件编写组件测试。

执行下一个命令,将你的终端目录改为react-components 文件夹,并启动交互式 Playwright CLI:
cd react-components
yarn create playwright --ct
上述命令中附加的--ct 标志将引导Playwright CLI设置测试,特别是测试组件。
在设置过程中,选择JavaScript 作为编写E2E测试的编程语言,并选择React 作为前端框架选项。
所有由 Playwright 生成的配置将被存储在playwright-ct.config.js 文件中,如果你想改变默认配置,可以调整该文件:

作为设置过程的一部分,Playwright将自动安装@playwright/experimental-ct-react 包,以使用组件测试功能。
至此,Playwright已经在react-components 应用程序中设置好了。
使用你的代码编辑器打开整个playwright-frontend-e2e-tests 项目,因为你将在下一节开始创建你的第一个测试案例。
为React编写Playwright组件测试
使用你的代码编辑器,在react-components/src 目录中创建一个名为tests 的目录。tests 文件夹将包含你将为 Playwright 创建的所有测试文件。
接下来,在测试目录下创建一个SearchField.spec.jsx 文件。该文件将包含SearchField 组件的测试,这些测试是用来搜索一个字符的。
Playwright将文件名扩展名前缀为.spec 的文件识别为测试文件。
将下面的代码块内容添加到SearchField.spec.jsx 文件中,以断言SearchField 组件中的输入字段可以接受文本值。
下面的测试案例使用 [locator](https://playwright.dev/docs/api/class-locator) 方法找到具有search-field id属性的HTML元素,用文本Rick ,并断言输入元素的值是 "Rick":
import { test, expect } from '@playwright/experimental-ct-react';
import App, { SearchField } from '../App';
test('SearchField accepts text input', async ({ mount }) => {
const searchComponent = await mount(<SearchField /> );
const searchField = searchComponent.locator('#search-field')
await searchField.fill('Rick')
await expect(searchField).toHaveValue('Rick')
});
接下来,将下面的代码添加到SearchField.spec.jsx 文件中,以断言当组件中的按钮被点击时,executeSearch 道具被执行:
test('Click on `Find Character` button executes executeSearch prop', async ({ mount }) => {
let isCalled = false
const searchComponent = await mount(
<SearchField
executeSearch={() => isCalled = true}
/>
);
await searchComponent.locator('#search-field').fill('test character')
await searchComponent.locator('#find').click()
expect(isCalled).toBeTruthy()
});
作为良好用户体验的一部分,表单中的按钮应该被禁用,直到表单中的强制输入字段被填满。
添加下面的代码,断言当输入字段为空时,该组件的按钮被禁用:
test('Input field text length controls `Find Character` button disabled state', async ({ mount }) => {
const searchComponent = await mount( <SearchField /> );
const btn = await searchComponent.locator('#find').isDisabled()
expect(btn).toBeTruthy()
await searchComponent.locator('#search-field').fill('test character')
await expect(searchComponent.locator('#find')).toBeEnabled();
});
这个测试案例是必要的,以确保只有在用户输入文本时才会触发搜索。
随着上述代码的添加,你现在在SearchField.spec.jsx 文件中有三个测试用例。让我们继续执行它们!
使用下面的命令执行你为SearchField 组件创建的测试用例:
yarn test-ct
你应该看到如下图所示的东西:

默认情况下,组件测试将在无头模式下执行,无需打开Chromium、Webkit和Firefox浏览器。在yarn test-ct 命令中添加--headless 标志,以启动这三个浏览器并查看正在测试的组件。
Playwright有一个内置功能,为每次测试运行生成并提供一个HTML报告。该HTML报告包含测试名称,执行状态,和每个测试案例的持续时间。
使用Playwright的show-report 命令为你的测试生成一个HTML报告:
npx playwright show-report
Playwright将在端口9323 ,为HTML报告启动一个本地服务器。使用你的浏览器,导航到 [http://localhost:9323](http://localhost:9323)来查看HTML报告:

在这一点上,你已经看到使用React构建的组件是如何直接使用Playwright进行测试的。让我们进一步为项目中的vue-components 应用程序设置Playwright。
为Vue组件设置Playwright组件测试
在上一节中,你在react-components 应用程序中使用Playwright来测试SearchField 组件。
在本节中,你将在vue-components 应用程序中以类似的方式使用Playwright来测试Character 组件,该组件显示Rick and Morty API中单个角色的详细信息。
执行下一个命令,将你的终端目录改为vue-components 目录,并运行Playwright安装程序:
cd ../vue-components
yarn create playwright --ct
对于安装提示,确保选择Vue作为前端框架。选择Vue将导致Playwright CLI自动安装@playwright/experimental-ct-vue 包,用于编写组件测试。
在接下来的步骤中,你将在vue-components 应用程序中为Character 组件编写两个测试。Character 组件在一个网格列表中呈现,使用从父组件接收的道具显示图片和Rick and Morty角色的一些细节。
下图中的高亮框显示了Character 组件的一个例子:

为Vue编写Playwright组件测试
在vue-components/src 目录中创建一个名为tests 的目录。正如你对react-components 应用程序所做的那样,vue-components 的测试文件也将存储在测试目录中。
在测试目录下创建一个Character.spec.js 文件,以存储Character 组件的测试案例。
将下面的代码块内容添加到Character.spec.js 文件中,以断言Character 组件将传入该组件的道具显示为字符的细节:
import { test, expect } from '@playwright/experimental-ct-vue';
import Character from '../components/Character.vue'
const SAMPLE_CHARACTER = {
"name": "Toxic Rick",
"gender": "Male",
"specie": "Humanoid",
"type": "Rick's Toxic Side",
"link": "https://rickandmortyapi.com/api/location/64",
"image": "https://rickandmortyapi.com/api/character/avatar/361.jpeg"
}
test('Component displays character details from props', async ({ mount }) => {
const characterComponent = await mount(Character, {
props: { ...SAMPLE_CHARACTER }
});
await expect(characterComponent.locator('#character-name')).toHaveText(SAMPLE_CHARACTER.name);
await expect(characterComponent.locator('#character-gender')).toHaveText(SAMPLE_CHARACTER.gender);
await expect(characterComponent.locator('#character-type')).toHaveText(SAMPLE_CHARACTER.type);
await expect(characterComponent.locator('#character-specie')).toHaveText(SAMPLE_CHARACTER.specie);
});
上面的代码包含了一个角色的样本字段的对象,类似于从对Rick and Morty API的实际请求中获取的内容。
接下来,添加下面的第二个测试案例,以断言Character 组件通过具有href属性的锚元素显示角色的名字:
test('Character name renders anchor link to character', async ({ mount }) => {
const characterComponent = await mount(Character, {
props: { ...SAMPLE_CHARACTER }
});
await expect(characterComponent.locator('#character-name > a') ).toHaveAttribute("href")
});
现在,执行test-ct 命令来运行你用上面两个代码块创建的两个Playwright测试案例:
yarn test-ct
和上一节一样,然后你应该能够看到你的测试结果,并为你的测试生成一个HTML报告。
为Svelte组件设置Playwright组件测试
在本文的前几节中,你已经在React和Vue应用程序中使用了Playwright,现在你在Svelte应用程序中使用Playwright。
首先,执行以下命令,将你的终端目录改为svelte-components ,并运行Playwright安装程序:
cd ../svelte-components
yarn create playwright --ct
当你通过安装提示时,确保选择Svelte作为前端框架。选择Svelte将导致Playwright自动安装@playwright/experimental-ct-svelte 。
在下一部分,你将为Paginator 组件编写两个测试用例,该组件将显示页码的下拉菜单。当用户点击Paginator 组件中列出的页面时,他们将被带到查看该页面上列出的字符,如下图所示。

为Svelte编写Playwright组件测试
在svelte-components/src 目录下创建一个名为tests 的目录,以存储包含你的Playwright测试案例的文件。
接下来,在tests 目录下创建一个Paginator.spec.js 文件,以存储Paginator 组件的测试用例。
将下面的代码块的内容添加到Paginator.spec.js 文件中。这段代码将创建一个测试用例,断言Paginator 组件将根据通过pagesCount 道具传递的数字显示选项元素:
import { test, expect } from '@playwright/experimental-ct-svelte'
import Paginator from '../src/components/Paginator.svelte'
test('Displays pages option based on pagesCount prop', async ({ mount }) => {
const component = await mount(Paginator, {
props: {
pagesCount: 42
}
})
expect(await component.locator('#pages-option > option').count()).toBe(42)})
})
添加下面的第二个测试用例,断言当点击选项时,Paginator组件的handlePageSelect 道具会返回一个数字,以按数字分页显示Rick and Morty字符:
test('`handlePageSelect` prop returns the pages number when clicked', async ({ mount }) => {
let pageClicked;
const component = await mount(Paginator, {
props: {
handlePageSelect: number => pageClicked = number,
pagesCount: 10,
}
})
await component.locator('#pages-option > option').first().click()
expect(pageClicked).toBe(1)
})
最后,运行test-ct Paginator组件的两个测试用例:
yarn test-ct

这就是了!
在通过Svelte应用程序的这些最后的Playwright组件测试后,你已经成功地使用了Playwright的组件测试功能来测试这个示例项目中的React、Vue和Svelte应用程序。
关于Playwright组件测试的最终想法
在本教程的开始,你着手学习如何用Playwright测试你的前端组件。
该教程解释了什么是Playright组件测试功能,并带你通过使用一个包含React、Vue和Svelte组件的monorepo项目进行组件测试的实践。
如前所述,重要的是要记住,Playwright的组件测试功能相对较新,仍被认为是实验性的。它还没有对Angular的支持,与其他已经存在多年的测试工具相比,你可能会发现有些断言对Playwright是不可用的。