使用TestCafe对Angular应用程序进行E2E测试
TestCafe 是一个免费的开源 Node.js 工具,用于测试 Web 应用程序。 它的一个主要优点是设置和开始测试大约需要一分钟(并且它不使用 WebDriver)。
它适用于大多数流行的操作系统和浏览器。 测试是用 JavaScript 或 TypeScript 编写的。
在本文中,我们将专门介绍测试 Angular 应用程序。 你将学到如何:
- 设置测试环境
- 使用 Angular 选择器创建基本测试
- 使用页面模型模式改进您的测试
- 调试失败的测试
测试设置
首先,确保您安装了 Node.js 4.x 或更高版本。
我们需要两个 npm 模块:TestCafe 本身,以及一个带有 Angular 选择器的插件。 运行此命令:
$ npm i testcafe testcafe-angular-selectors
让我们也将 Angular Augury 扩展安装到 Google Chrome。 我们将使用它在应用程序的组件树中查找组件选择器。
就是这样,我们准备好测试了!
编写一个简单的测试
我们将测试一个示例 Book Collection Angular 应用程序,在此处发布:https://miherlosev.github.io/e2e_angular
。 我们的第一个测试将只是一个登录表单。
请注意,testcafe-angular-selectors
和 Angular Augry 需要在开发模式下运行的应用程序。
让我们创建一个 index.js
文件并添加以下代码:
index.js
import { AngularSelector, waitForAngular } from 'testcafe-angular-selectors'; fixture `Book Collection` .page('https://miherlosev.github.io/e2e_angular/') .beforeEach(async t => { await waitForAngular(); }); test('Login', async t => { const loginForm = AngularSelector('bc-login-form'); await t .typeText(loginForm.find('#md-input-1'), 'test') .typeText(loginForm.find('#md-input-3'), 'test') .click(loginForm.find('.mat-button')); });
这就是发生的事情。 我们从导入 Angular 选择器开始。 我们将使用它们来处理带有 组件选择器 的页面元素。
在夹具中,我们指定测试页面并使用 beforeEach
测试挂钩。 当您需要在每次测试运行之前执行特定操作时,此挂钩非常有用。 在我们的例子中,waitForAngular
方法允许我们等待 Angular 组件树被加载。 之后,我们的测试可以通过组件名称找到登录和密码字段,填写它们并按下登录按钮。
让我们运行我们的测试。 创建一个 package.json
文件,内容如下:
包.json
{ "scripts": { "test": "testcafe chrome index.js" } }
test
任务从 Google Chrome 中的 index.js
运行测试。 现在运行这个命令:
$ npm test
TestCafe 的报告显示我们的测试已经通过。
使用页面模型模式改进测试
Page Model 是一种模式,您可以在其中创建测试页面的抽象,并使用它来引用页面元素。 如果页面标记发生更改,它有助于节省支持测试的时间。
让我们为登录页面创建一个页面模型。 该页面包含三个元素:用户名和密码输入,以及登录按钮。 我们将整个页面表示为一个 JavaScript 类,并将其元素表示为选择器。
创建一个 page-model.js
文件并添加以下代码:
页面模型.js
import { AngularSelector } from 'testcafe-angular-selectors'; export class LoginPage { constructor () { const loginForm = AngularSelector('bc-login-form'); this.username = loginForm.find('#md-input-1'); this.password = loginForm.find('#md-input-3'); this.loginBtn = loginForm.find('.mat-button'); } }
我们使用 AngularSelector
构造函数。 您可以将由空格分隔的 组件选择器 传递给此构造函数,它会返回一个本机 TestCafe 选择器。 TestCafe 选择器允许按标签名称、ID 等进行额外过滤。 在我们的示例中,我们使用它的 find
方法来定位文本字段和按钮。
现在让我们重构 index.js
。 我们从测试中提取了与选择器相关的代码并将其移至页面模型中。 在此之后,测试代码仅包含与页面元素的交互:
import { waitForAngular } from 'testcafe-angular-selectors'; import { LoginPage } from './page-model.js'; const loginPage = new LoginPage(); fixture `Book Collection` .page('https://miherlosev.github.io/e2e_angular/') .beforeEach(async t => { await waitForAngular(); }); test('Login', async t => { await t .typeText(loginPage.username, 'test') .typeText(loginPage.password, 'test') .click(loginPage.loginBtn); });
使用相同的命令运行测试,并查看它是否像以前一样工作。
创建更复杂的测试
我们的测试目前实际上并没有检查任何东西。 让我们添加一个断言:修改测试以查找特定书籍,并检查搜索结果是否为空。
我们将继续使用页面模型模式,所以让我们更新 page-model.js
。 首先,在开头再添加一个import
:
import { t } from 'testcafe';
t
是一个名为 TestContext
的 TestCafe 对象,它允许执行各种操作,例如单击和键入。
然后在最后添加以下内容:
页面模型.js
class BasePage { constructor () { const navigationItem = AngularSelector('bc-nav-item'); this.toolbar = AngularSelector('bc-toolbar'); this.sidenav = { myCollectionNavItem: navigationItem.find('a').withText('My Collection'), browseBooksNavItem: navigationItem.find('a').withText('Browse Books') }; this.openToolbarBtn = this.toolbar.find('button'); } async navigateToBrowseBooksPage () { await t .click(this.openToolbarBtn) .click(this.sidenav.browseBooksNavItem); } } export class FindBookPage extends BasePage { constructor () { super(); this.searchInput = AngularSelector('bc-book-search').find('input'); this.bookPreviews = AngularSelector('bc-book-preview'); } }
BasePage
类处理在其他页面(工具栏和主菜单)之间共享的 UI 元素。 对于所有其他页面,例如 FindBookPage
,我们可以扩展这个类。
现在修改 index.js
看起来像这样:
import { waitForAngular } from 'testcafe-angular-selectors'; import { LoginPage, FindBookPage, } from './page-model'; const loginPage = new LoginPage(); const findBookPage = new FindBookPage(); fixture `Book Collection` .page('https://miherlosev.github.io/e2e_angular/') .beforeEach(async t => { await waitForAngular(); await t .typeText(loginPage.username, 'test') .typeText(loginPage.password, 'test') .click(loginPage.loginBtn); }); test("find a book", async t => { await findBookPage.navigateToBrowseBooksPage(); await t .typeText(findBookPage.searchInput, 'The Hunger Games') .expect(findBookPage.bookPreviews.count).gt(0); });
请注意,我们已将授权移动到 beforeEach
测试挂钩中。 我们这样做是因为我们的应用程序需要授权,并且 TestCafe 在干净的环境中运行每个测试以避免不稳定的测试。 为了本教程的目的,我们将保持简单,但还有一种更高级的授权机制,称为 User Roles。 它允许隔离身份验证并在您需要切换用户帐户时应用它。
如您所见,代码仅包含测试逻辑。 我们不需要等待页面元素加载、动画、XHR 完成或任何其他样板代码。
现在让我们用 npm test
运行测试并查看结果。
测试通过了。 默认情况下,TestCafe 在控制台中显示测试结果。 还有一些插件为 TeamCity、Slack 等提供自定义报告器。
测试失败时定位错误
让我们看看失败的测试是什么样子的。 更改最后一个断言,使测试失败:
.expect(findBookPage.bookPreviews.count).eql(0);
现在运行测试。
对于每个失败的测试,TestCafe 都会突出显示失败的步骤。 它还报告浏览器、调用站点和其他详细信息,让您快速找到失败的原因。 要修复测试,请改回代码。
接下来是什么:有用的链接
在本教程中,我们使用 TestCafe 和页面模型模式进行了简单的测试。 有关更多示例和建议,请查看 官方文档 。
您可以在 GitHub 上找到本教程 的完整 代码。
如果您对 TestCafe 有任何疑问,请随时在 论坛 上提问。 有关功能请求和错误,请转到 GitHub 问题页面 。