软件测试

什么是测试?

软件测试是为向利益相关者提供有关被测软件产品或服务质量的信息而进行的调查。

[图片上传中...(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只是语法糖。最重要的线,expecttoEqualdescribe并将it测试分解为打印到终端的逻辑块。该expect函数接受我们想要验证的输入,同时toEqual接受所需的输出。您可以使用许多不同的函数和方法来测试您的应用程序。

假设我们正在使用Jest,一个用于编写单元的库。在上面的示例中,Jest 将sayHello在终端中将该函数显示为标题。it函数内的所有内容都被视为单个测试,并在函数标题下方的终端中报告,使所有内容都非常易于阅读。

[图片上传失败...(image-6ebac0-1622560751592)]

<figcaption style="box-sizing: border-box; display: block; font-size: 0.7rem; color: rgb(118, 118, 118); width: 822.391px; padding-top: 0.5rem;">绿色复选标记表示我们的两项测试均已通过。耶!</figcaption>

集成测试

  • 等级:中等
  • 范围:测试单元之间的交互。
  • 可能的工具: AVAJest测试库

[图片上传失败...(image-60e861-1622560751592)]

如果单元测试检查块的行为,集成测试可确保块完美地协同工作。这使得集成测试变得非常重要,因为它开启了组件之间的测试交互。应用程序由独立运行的独立部分组成,这种情况非常罕见(如果有的话)。这就是我们依赖集成测试的原因。

我们回到我们单元测试的函数,但这次在一个简单的 React 应用程序中使用它。假设单击按钮会触发问候语出现在屏幕上。这意味着测试不仅涉及功能,还涉及 HTML DOM 和按钮的功能。我们想测试所有这些部分如何协同工作。

这是<Greeting />我们正在测试的组件的代码:

export const Greeting = () => {  
  const [showGreeting, setShowGreeting] = useState(false);  

 return (  
   <div>  
     <p data-testid="greeting">{showGreeting && sayHello()}</p>  
     <button data-testid="show-greeting-button" onClick={() => setShowGreeting(true)}>Show Greeting</button>  
   </div>
 );  
};

这是集成测试:

describe('<Greeting />', () => {  
  it('shows correct greeting', () => {  
    const screen = render(<Greeting />);  
     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中显示<Greeting />组件的功能,因此我们可以在不接触真实 DOM 的情况下测试与组件的交互——否则,它可能会很昂贵。

接下来,通过测试 ID(分别为和)的测试查询<p><button>元素。我们使用测试 ID 是因为更容易从模拟 DOM 中获取我们想要的组件。还有其他查询组件的方法,但这是我最常用的方法。#greeting``#show-greeting-button

直到第 7 行才开始真正的集成测试!我们首先检查<p>标签是否为空。然后我们通过模拟一个click事件来点击按钮。最后,我们检查<p>标签是否包含“Hello human!” 在里面。就是这样!我们正在测试的只是一个空段落在单击按钮后包含文本。我们的组件被覆盖。

当然,我们可以在有人输入姓名的地方添加输入,然后在问候功能中使用该输入。但是,我决定让它更简单一些。当我们涵盖其他类型的测试时,我们将开始使用输入。

查看运行集成测试时我们在终端中得到的信息:

[图片上传失败...(image-6e51ad-1622560751591)]

<figcaption style="box-sizing: border-box; display: block; font-size: 0.7rem; color: rgb(118, 118, 118); width: 822.391px; padding-top: 0.5rem;">完美的!<Greeting /> 单击按钮时,该 组件会显示正确的问候语。</figcaption>

端到端 (E2E) 测试

  • 等级:
  • 范围:通过提供有关操作和预期结果的说明,在真实浏览器中测试用户交互。
  • 可能的工具: CypressPuppeteer

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 来获取导航按钮,然后指示测试点击它。该操作将导航到包含该<Greetings />组件的页面。事实上,我已将该组件添加到我的个人网站并为其提供了自己的 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

可访问性测试

Web 可访问性意味着设计和开发网站、工具和技术,以便残障人士可以使用它们。

<cite style="box-sizing: border-box; display: block; text-indent: 2rem;">W3C</cite>

可访问性测试确保残障人士可以有效地访问和使用网站。这些测试验证您是否遵循构建网站时考虑到可访问性的标准。

例如,许多没有视力的人使用屏幕阅读器。屏幕阅读器扫描您的网站并尝试以用户可以理解的格式(通常是口语)将其呈现给残障用户。作为开发人员,您希望让屏幕阅读器的工作变得轻松,而可访问性测试将帮助您了解从哪里开始。

很多不同的工具,其中一些是自动化的,一些是手动运行以验证可访问性。例如,Chrome 已经在其 DevTools 中内置了一个工具。你可能知道它是灯塔

[图片上传失败...(image-acdb24-1622560751590)]

让我们使用 Lighthouse 来验证我们在 E2E 测试部分中制作的应用程序。我们在 Chrome DevTools 中打开 Lighthouse,点击“Accessibility”测试选项,然后“Generate”报告。

[图片上传失败...(image-c73135-1622560751590)]

这就是我们要做的所有事情!Lighthouse 完成它的工作,然后生成一份可爱的报告,并附有分数、运行的审计摘要以及提高分数的机会大纲。

[图片上传失败...(image-c007-1622560751590)]ruanj

但这只是从其特定角度衡量可访问性的一种工具。我们拥有各种可访问性工具,值得为要测试的内容和可用于达到这些目标的工具制定计划。

视觉回归测试

  • 等级:
  • 范围:测试应用程序的视觉结构,包括代码更改产生的视觉差异。
  • 可能的工具: CypressPercyApplitools

有时 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 进行比较。这就对了!测试完成后,我们将收到一个链接来检查我们的回归。这是我在终端中得到的:

有些东西明显改变了,需要修复。

性能测试

性能测试非常适合检查应用程序的速度。如果性能对您的业务至关重要——而且它可能是最近对 Core Web Vitals 和 SEO 的关注 ——您肯定想知道对代码库的更改是否对应用程序的速度产生负面影响。

我们可以将其烘焙到我们的其余测试流程中,或者我们可以手动运行它们。如何运行这些测试以及运行它们的频率完全取决于您。一些开发人员创建所谓的“性能预算”并运行计算应用程序大小的测试,如果大小超过某个阈值,将阻止部署发生。或者,经常使用 Lighthouse 进行手动测试,因为它还可以衡量性能指标。或者将两者结合起来,将Lighthouse 构建到测试套件中

性能测试可以测量任何与性能相关的东西。它们可以测量应用程序加载的速度、其初始包的大小,甚至可以测量特定功能的速度。性能测试有点广阔,广阔的领域。

这是使用 Lighthouse 的快速测试。我认为这是一个很好的展示,因为它专注于 Core Web Vitals 以及如何在 Chrome 的 DevTools 中轻松访问它而无需任何安装或配置。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容