什么是测试?
软件测试是为向利益相关者提供有关被测软件产品或服务质量的信息而进行的调查。
[图片上传中...(image-2e77df-1622560751592-12)]
从最基本的意义上说,测试是一种自动化工具,可以尽早发现开发中的错误。这样,您就可以在它们投入生产之前解决这些问题。测试还可以提醒您,您可能忘记检查自己在某个领域的工作,例如可访问性。
简而言之,前端测试验证人们在网站上看到的内容以及他们在网站上使用的功能是否按预期工作。
前端测试适用于应用程序的客户端。例如,前端测试可以验证按下“删除”按钮是否正确地从屏幕上删除了一个项目。但是,它不一定会检查该项目是否确实从数据库中删除了 — 后端测试期间将涵盖此类事情。
简而言之,这就是测试:我们希望在客户端捕获错误并在部署代码之前修复它们。
不同的测试着眼于项目的不同部分
[图片上传失败...(image-cd4801-1622560751592)]
不同类型的测试涵盖项目的不同方面。然而,区分它们并了解每种类型的作用很重要。混淆哪些测试会导致混乱、不可靠的测试服。
理想情况下,您会使用几种不同类型的测试来揭示不同类型的可能问题。某些测试类型具有测试覆盖率分析,可显示该特定测试查看了多少代码(以百分比形式)。这是一个很棒的功能,虽然我已经看到开发人员的目标是 100% 的覆盖率,但我不会仅仅依赖这个指标。最重要的是确保涵盖并考虑所有可能的边缘情况。
所以,有了这个,让我们把注意力转向不同类型的测试。请记住,并不是希望您使用其中的每一个。这是关于能够区分测试,以便您知道在某些情况下使用哪些测试。
单元测试
- 等级:低
- 范围:测试应用程序的功能和方法。
- 可能的工具: , AVA , Jasmine , Jest , Karma , Mocha
单元测试是测试最基本的构建块。它查看单个组件并确保它们按预期工作。这种测试对于任何前端应用程序都是至关重要的,因为有了它,您的组件将根据预期的行为方式进行测试,从而产生更可靠的代码库和应用程序。这也是可以考虑和涵盖边缘情况之类的地方。
单元测试特别适合测试 API。但不是调用实时 API,而是硬编码(或“模拟”)数据确保您的测试运行始终保持一致。
让我们以一个超级简单(和原始)的函数为例:
const sayHello = (name) => {
if (!name) {
return "Hello human!";
}
return `Hello ${name}!`;
};
同样,这是一个基本情况,但您可以看到它涵盖了一个小的边缘情况,即有人可能忽略了为应用程序提供名字。如果有name
,我们会得到“你好${name}
!” ${name}
我们期望此人提供的东西在哪里。
“嗯,我们为什么要测试这么小的东西?” 你可能想知道。这有一些非常重要的原因:
- 它迫使你深入思考你的函数可能产生的结果。通常情况下,您确实会发现边缘情况,这有助于您在代码中覆盖它们。
- 你的代码的某些部分可以依赖于这种边缘情况,如果有人来删除了一些重要的东西,测试会警告他们这个代码很重要并且不能被删除。
单元测试通常小而简单。下面是一个例子:
describe("sayHello function", () => {
it("should return the proper greeting when a user doesn't pass a name", () => {
expect(sayHello()).toEqual("Hello human!")
})
it("should return the proper greeting with the name passed", () => {
expect(sayHello("Evgeny")).toEqual("Hello Evgeny!")
})
})
describe
并且it
只是语法糖。最重要的线,expect
和toEqual
。describe
并将it
测试分解为打印到终端的逻辑块。该expect
函数接受我们想要验证的输入,同时toEqual
接受所需的输出。您可以使用许多不同的函数和方法来测试您的应用程序。
假设我们正在使用Jest,一个用于编写单元的库。在上面的示例中,Jest 将sayHello
在终端中将该函数显示为标题。it
函数内的所有内容都被视为单个测试,并在函数标题下方的终端中报告,使所有内容都非常易于阅读。
[图片上传失败...(image-6ebac0-1622560751592)]
集成测试
- 等级:中等
- 范围:测试单元之间的交互。
- 可能的工具: AVA、Jest、测试库
[图片上传失败...(image-60e861-1622560751592)]
如果单元测试检查块的行为,集成测试可确保块完美地协同工作。这使得集成测试变得非常重要,因为它开启了组件之间的测试交互。应用程序由独立运行的独立部分组成,这种情况非常罕见(如果有的话)。这就是我们依赖集成测试的原因。
我们回到我们单元测试的函数,但这次在一个简单的 React 应用程序中使用它。假设单击按钮会触发问候语出现在屏幕上。这意味着测试不仅涉及功能,还涉及 HTML DOM 和按钮的功能。我们想测试所有这些部分如何协同工作。
这是
我们正在测试的组件的代码:
export const Greeting = () => {
const [showGreeting, setShowGreeting] = useState(false);
return (
{showGreeting && sayHello()}
);
};
这是集成测试:
describe(' ', () => {
it('shows correct greeting', () => {
const screen = render( );
const greeting = screen.getByTestId('greeting');
const button = screen.getByTestId('show-greeting-button');
expect(greeting.textContent).toBe('');
fireEvent.click(button);
expect(greeting.textContent).toBe('Hello human!');
});
});
我们已经知道describe
,并it
从我们的单元测试。他们将测试分解为逻辑部分。我们有在特殊的模拟 DOMrender
中显示
组件的功能,因此我们可以在不接触真实 DOM 的情况下测试与组件的交互——否则,它可能会很昂贵。
接下来,通过测试 ID(分别为和)的测试查询和
元素。我们使用测试 ID 是因为更容易从模拟 DOM 中获取我们想要的组件。还有其他查询组件的方法,但这是我最常用的方法。
#greeting``#show-greeting-button
直到第 7 行才开始真正的集成测试!我们首先检查标签是否为空。然后我们通过模拟一个
click
事件来点击按钮。最后,我们检查标签是否包含“Hello human!” 在里面。就是这样!我们正在测试的只是一个空段落在单击按钮后包含文本。我们的组件被覆盖。
当然,我们可以在有人输入姓名的地方添加输入,然后在问候功能中使用该输入。但是,我决定让它更简单一些。当我们涵盖其他类型的测试时,我们将开始使用输入。
查看运行集成测试时我们在终端中得到的信息:
[图片上传失败...(image-6e51ad-1622560751591)]
单击按钮时,该 组件会显示正确的问候语。
端到端 (E2E) 测试
- 等级:高
- 范围:通过提供有关操作和预期结果的说明,在真实浏览器中测试用户交互。
- 可能的工具: Cypress、Puppeteer
E2E 测试是此列表中最高级别的测试。E2E 测试只关心人们如何看待您的应用程序以及他们如何与之交互。他们对代码和实现一无所知。
E2E 测试告诉浏览器做什么、点击什么和输入什么。我们可以创建各种交互,在最终用户体验时测试不同的功能和流程。它实际上是一个机器人,通过交互来点击应用程序以确保一切正常。
E2E 测试在某种程度上类似于集成测试。但是,E2E 测试是在具有真实 DOM 的真实浏览器中执行的,而不是我们模拟的东西——我们通常在这些测试中使用真实数据和真实 API。
全面覆盖单元和集成测试是很好的。但是,用户在浏览器中运行应用程序时可能会遇到意外行为——端到端测试是完美的解决方案。
让我们看一个使用Cypress的示例,这是一个非常流行的测试库。我们将专门使用它来对我们之前的组件进行 E2E 测试,这次是在具有一些额外功能的浏览器中。
同样,我们不需要查看应用程序的代码。我们所假设的只是我们有一些应用程序,我们想以用户身份对其进行测试。我们知道要单击哪些按钮以及这些按钮的 ID。这就是我们真正需要离开的。
describe('Greetings functionality', () => {
it('should navigate to greetings page and confirm it works', () => {
cy.visit('http://localhost:3000')
cy.get('#greeting-nav-button').click()
cy.get('#greetings-input').type('Evgeny', { delay: 400 })
cy.get('#greetings-show-button').click()
cy.get('#greeting-text').should('include.text', 'Hello Evgeny!')
})
})
这个 E2E 测试看起来与我们之前的集成测试非常相似。这些命令极其相似,主要区别在于它们是在真实浏览器中执行的。
首先,我们使用cy.visit
导航到我们的应用程序所在的特定 URL:
cy.visit('http://localhost:3000')
其次,我们使用cy.get
它的 ID 来获取导航按钮,然后指示测试点击它。该操作将导航到包含该/>
组件的页面。事实上,我已将该组件添加到我的个人网站并为其提供了自己的 URL 路由。
cy.get('#greeting-nav-button').click()
然后,依次输入文本,输入“Evgeny”,单击#greetings-show-button
按钮,最后检查我们是否获得了所需的问候语输出。
cy.get('#greetings-input').type('Evgeny', { delay: 400 })
cy.get('#greetings-show-button').click()
cy.get('#greeting-text').should('include.text', 'Hello Evgeny!')
在真实的实时浏览器中观看测试如何为您点击按钮非常酷。我把测试放慢了一点,这样你就可以看到发生了什么。所有这些通常发生得非常快。
[图片上传中...(image-fe45b4-1622560751591-7)]
这是终端输出:
[图片上传失败...(image-3eceb0-1622560751591)]rua
可访问性测试
- 等级:高
- 范围:根据可访问性标准标准测试应用程序的界面。
- 可能的工具: AccessLint、axe-core、Lighthouse、pa11y
Web 可访问性意味着设计和开发网站、工具和技术,以便残障人士可以使用它们。
W3C
可访问性测试确保残障人士可以有效地访问和使用网站。这些测试验证您是否遵循构建网站时考虑到可访问性的标准。
例如,许多没有视力的人使用屏幕阅读器。屏幕阅读器扫描您的网站并尝试以用户可以理解的格式(通常是口语)将其呈现给残障用户。作为开发人员,您希望让屏幕阅读器的工作变得轻松,而可访问性测试将帮助您了解从哪里开始。
有很多不同的工具,其中一些是自动化的,一些是手动运行以验证可访问性。例如,Chrome 已经在其 DevTools 中内置了一个工具。你可能知道它是灯塔。
[图片上传失败...(image-acdb24-1622560751590)]
让我们使用 Lighthouse 来验证我们在 E2E 测试部分中制作的应用程序。我们在 Chrome DevTools 中打开 Lighthouse,点击“Accessibility”测试选项,然后“Generate”报告。
[图片上传失败...(image-c73135-1622560751590)]
这就是我们要做的所有事情!Lighthouse 完成它的工作,然后生成一份可爱的报告,并附有分数、运行的审计摘要以及提高分数的机会大纲。
[图片上传失败...(image-c007-1622560751590)]ruanj
但这只是从其特定角度衡量可访问性的一种工具。我们拥有各种可访问性工具,值得为要测试的内容和可用于达到这些目标的工具制定计划。
视觉回归测试
- 等级:高
- 范围:测试应用程序的视觉结构,包括代码更改产生的视觉差异。
- 可能的工具: Cypress、Percy、Applitools
有时 E2E 测试不足以验证对应用程序的最后更改没有破坏界面中任何内容的视觉外观。您是否只是为了意识到它破坏了应用程序其他部分的布局而将代码进行了一些更改推送到生产中?那么,你并不孤单。大多数情况下,对代码库的更改会破坏应用程序的视觉结构或布局。
解决方案是视觉回归测试。它的工作方式非常简单。可视化测试只是截取页面或组件的屏幕截图,并将它们与之前成功测试中捕获的屏幕截图进行比较。如果这些测试发现屏幕截图之间存在任何差异,他们会向我们发出某种通知。
让我们转向一个名为Percy的可视化回归工具,看看可视化回归测试是如何工作的。有很多其他方法可以进行视觉回归测试,但我认为 Percy 很容易在行动中展示。事实上,你可以在 CSS-Tricks 上跳到Paul Ryan 对 Percy 的深入探讨。但是我们会做一些相当简单的事情来说明这个概念。
我故意通过将按钮移动到输入底部来破坏我们的 Greeting 应用程序的布局。让我们试着用 Percy 来捕捉这个错误。
Percy 与 Cypress 配合良好,因此我们可以按照他们的安装指南运行 Percy 回归测试以及我们现有的 E2E 测试。
describe('Greetings functionality', () => {
it('should navigate to greetings page and confirm everything is there', () => {
cy.visit('http://localhost:3000')
cy.get('#greeting-nav-button').click()
cy.get('#greetings-input').type('Evgeny', { delay: 400 })
cy.get('#greetings-show-button').click()
cy.get('#greeting-text').should('include.text', 'Hello Evgeny!')
// Percy test
cy.percySnapshot() // HIGHLIGHT
})
})
我们在 E2E 测试结束时添加的只是一个单行:cy.percySnapshot()
. 这将截取屏幕截图并将其发送给 Percy 进行比较。这就对了!测试完成后,我们将收到一个链接来检查我们的回归。这是我在终端中得到的:
有些东西明显改变了,需要修复。
性能测试
- 等级:高
- 范围:测试应用程序的性能和稳定性。
- 可能的工具: Lighthouse、PageSpeed Insights、WebPageTest、YSlow
性能测试非常适合检查应用程序的速度。如果性能对您的业务至关重要——而且它可能是最近对 Core Web Vitals 和 SEO 的关注 ——您肯定想知道对代码库的更改是否对应用程序的速度产生负面影响。
我们可以将其烘焙到我们的其余测试流程中,或者我们可以手动运行它们。如何运行这些测试以及运行它们的频率完全取决于您。一些开发人员创建所谓的“性能预算”并运行计算应用程序大小的测试,如果大小超过某个阈值,将阻止部署发生。或者,经常使用 Lighthouse 进行手动测试,因为它还可以衡量性能指标。或者将两者结合起来,将Lighthouse 构建到测试套件中。
性能测试可以测量任何与性能相关的东西。它们可以测量应用程序加载的速度、其初始包的大小,甚至可以测量特定功能的速度。性能测试有点广阔,广阔的领域。
这是使用 Lighthouse 的快速测试。我认为这是一个很好的展示,因为它专注于 Core Web Vitals 以及如何在 Chrome 的 DevTools 中轻松访问它而无需任何安装或配置。