使用 TestCafe 对 Angular 应用程序进行 E2E 测试

TestCafe是一个用于测试Web应用程序的免费开源Node.js工具。它的一个主要好处是,它需要大约一分钟的时间来设置和开始测试(它不使用WebDriver)。

它可以在大多数流行的操作系统和浏览器上运行。测试是用脚本或打字稿编写的。

在本文中,我们将专门介绍测试角度应用程序。您将学习如何:

  • 设置测试环境
  • 使用Angular选择器创建基本测试
  • 使用页面模型模式改进测试
  • 测试失败

测试设置

首先,确保您安装了Node.js4.x版或更高版本)。

我们需要两个NPM模块:TestCafe 本身,以及一个带有角度选择器的插件。运行以下命令:

1$ npm i testcafe testcafe-angular-selectors

让我们也为Google Chrome安装Angular Auury扩展。我们将使用它在应用程序的组件树中查找组件选择器。

角度Augury中的组件树视图

就这样,我们准备好测试了!

编写简单测试

我们将测试一个示例图书CollectionANGING应用程序,发布在此处:https://miherlosev.github.io/e2e_angular.我们的第一个测试将只是一个登录表单。

示例application登录页面

<$>[注意]请注意,testcafe-ang角-选择器和角度愤怒需要一个应用程序在开发模式下运行。<$>

让我们创建一个index.js文件,并添加以下代码:

 1[label index.js]
 2
 3import { AngularSelector, waitForAngular } from 'testcafe-angular-selectors';
 4
 5fixture `Book Collection`
 6  .page('https://miherlosev.github.io/e2e_angular/')
 7  .beforeEach(async t => {
 8    await waitForAngular();
 9  });
10
11test('Login', async t => {
12  const loginForm = AngularSelector('bc-login-form');
13
14  await t
15    .typeText(loginForm.find('#md-input-1'), 'test')
16    .typeText(loginForm.find('#md-input-3'), 'test')
17    .click(loginForm.find('.mat-button'));
18});

事情是这样的我们从导入Angular选择器开始。我们将使用它们来通过component selectors处理页面元素。

在装备中,我们指定测试页面并使用bepreEach测试挂钩。当您需要在每次测试运行前执行特定操作时,此挂钩非常有用。在我们的例子中,waitForAngular方法允许我们等待,直到加载了角度组件树。之后,我们的测试可以通过组件名称找到Login和Password字段,填写它们并按下Login按钮。

让我们运行我们的测试。创建包含以下内容的Package.json文件:

1[label package.json]
2{
3  "scripts": {
4    "test": "testcafe chrome index.js"
5  }
6}

test任务从Google Chrome中的index.js运行测试。现在运行以下命令:

1$ npm test

第一个测试具有passed

TestCafe的报告显示,我们的测试已经通过。

用页面模型模式改进测试

Page Model是一种模式,您可以在其中创建测试页面的抽象,并使用它来引用页面元素。它有助于在页面标记更改时节省支持测试的时间。

让我们为登录页面创建一个页面模型。该页面有三个元素:用户名和密码输入,以及登录按钮。我们将整个页面表示为一个JavaScript类,并将其元素表示为选择器。

创建一个page-Model.js文件,并添加以下代码:

 1[label page-model.js]
 2import { AngularSelector } from 'testcafe-angular-selectors';
 3
 4export class LoginPage {
 5  constructor () {
 6    const loginForm = AngularSelector('bc-login-form');
 7
 8    this.username = loginForm.find('#md-input-1');
 9    this.password = loginForm.find('#md-input-3');
10    this.loginBtn = loginForm.find('.mat-button');
11  }
12}

我们使用AngularSelector‘构造函数。您可以将以空格分隔的[Component selectors](https://angular.io/api/core/Component)]传递给此构造函数,它将返回一个本机TestCafe选择器。TestCafe选择器允许按标签名、ID等进行额外过滤。在我们的示例中,我们使用其find`方法来定位文本字段和按钮。

现在让我们重构index.js。我们从测试中提取了与选择器相关的代码,并将其移到页面模型中。在此之后,测试代码只包含与页面元素的交互:

 1import { waitForAngular } from 'testcafe-angular-selectors';
 2import { LoginPage } from './page-model.js';
 3
 4const loginPage = new LoginPage();
 5
 6fixture `Book Collection`
 7  .page('https://miherlosev.github.io/e2e_angular/')
 8  .beforeEach(async t => {
 9    await waitForAngular();
10});
11
12test('Login', async t => {
13  await t
14    .typeText(loginPage.username, 'test')
15    .typeText(loginPage.password, 'test')
16    .click(loginPage.loginBtn);
17});

使用相同的命令运行测试,并查看它是否像以前一样工作。

创建更复杂的测试

我们的测试目前并没有检查任何东西。让我们添加一个断言:修改测试以查找特定的书籍,并检查搜索结果是否为空。

我们将继续使用页面模型模式,因此让我们更新page-mod.js。首先,在开头多加一个port

1import { t } from 'testcafe';

t是一个名为TestConext的TestCafe对象,它允许执行各种操作,如点击和输入。

然后在结尾处添加以下内容:

 1[label page-model.js]
 2class BasePage {
 3  constructor () {
 4    const navigationItem = AngularSelector('bc-nav-item');
 5
 6    this.toolbar = AngularSelector('bc-toolbar');
 7    this.sidenav = {
 8      myCollectionNavItem: navigationItem.find('a').withText('My Collection'),
 9      browseBooksNavItem:  navigationItem.find('a').withText('Browse Books')
10    };
11
12    this.openToolbarBtn = this.toolbar.find('button');
13  }
14
15  async navigateToBrowseBooksPage () {
16    await t
17      .click(this.openToolbarBtn)
18      .click(this.sidenav.browseBooksNavItem);
19  }
20}
21
22export class FindBookPage extends BasePage {
23  constructor () {
24    super();
25
26    this.searchInput  = AngularSelector('bc-book-search').find('input');
27    this.bookPreviews = AngularSelector('bc-book-preview');
28  }
29}

BasePage类处理在其他页面(工具栏和主菜单)之间共享的UI元素。对于所有其他页面,例如FindBookPage,我们只需扩展这个类。

现在修改index.js如下:

 1import { waitForAngular } from 'testcafe-angular-selectors';
 2import { LoginPage, FindBookPage, } from './page-model';
 3
 4const loginPage = new LoginPage();
 5const findBookPage = new FindBookPage();
 6
 7fixture `Book Collection`
 8  .page('https://miherlosev.github.io/e2e_angular/')
 9  .beforeEach(async t => {
10    await waitForAngular();
11
12    await t
13      .typeText(loginPage.username, 'test')
14      .typeText(loginPage.password, 'test')
15      .click(loginPage.loginBtn);
16  });
17
18test("find a book", async t => {
19  await findBookPage.navigateToBrowseBooksPage();
20
21  await t
22    .typeText(findBookPage.searchInput, 'The Hunger Games')
23    .expect(findBookPage.bookPreviews.count).gt(0);
24});

请注意,我们已经将授权移到了bepreEach测试挂钩中。我们这样做是因为我们的应用程序需要授权,而TestCafe在干净的环境中运行每项测试,以避免出现不可靠的测试。出于本教程的目的,我们将保持简单,但还有一种更高级的授权机制,称为用户角色 。它允许隔离身份验证,并在您需要切换用户帐户时应用它。

如您所见,代码只包含测试逻辑。我们不需要等待页面元素加载、动画、XHR完成或任何其他样板代码。

现在让我们使用npm test运行测试,并查看结果。

第二个测试有passed

测试已经通过。默认情况下,TestCafe在控制台中显示测试结果。还有一些插件可以为TeamCity、Slack等提供定制的记者。

测试失败定位错误

让我们看看失败的测试是什么样子的。更改最后一个断言,以使测试失败:

1.expect(findBookPage.bookPreviews.count).eql(0);

现在运行测试。

测试咖啡馆突出显示测试failed

对于每个失败的测试,TestCafe会突出显示失败的步骤。它还报告浏览器、CallSite和其他详细信息,使您能够快速找到故障原因。要修复测试,请将代码改回。

下一步:有用的链接

在本教程中,我们使用TestCafe和页面模型模式进行了简单的测试。有关更多示例和建议,请查看官方documentation.

您可以在GitHub上找到此tutorial](https://github.com/miherlosev/e2e_angular2)的完整[代码。

如果您对TestCafe有任何疑问,请随时在论坛上提问。有关功能请求和错误,请转到GitHub问题页面

Published At
Categories with 技术
Tagged with
comments powered by Disqus