TestCafe是一个用于测试Web应用程序的免费开源Node.js工具。它的一个主要好处是,它需要大约一分钟的时间来设置和开始测试(它不使用WebDriver)。
它可以在大多数流行的操作系统和浏览器上运行。测试是用脚本或打字稿编写的。
在本文中,我们将专门介绍测试角度应用程序。您将学习如何:
- 设置测试环境
- 使用Angular选择器创建基本测试
- 使用页面模型模式改进测试
- 测试失败
测试设置
首先,确保您安装了Node.js4.x版或更高版本)。
我们需要两个NPM模块:TestCafe 本身,以及一个带有角度选择器的插件。运行以下命令:
1$ npm i testcafe testcafe-angular-selectors
让我们也为Google Chrome安装Angular Auury扩展。我们将使用它在应用程序的组件树中查找组件选择器。
中的组件树视图
就这样,我们准备好测试了!
编写简单测试
我们将测试一个示例图书CollectionANGING应用程序,发布在此处:https://miherlosev.github.io/e2e_angular
.我们的第一个测试将只是一个登录表单。
登录页面
<$>[注意]请注意,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
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
运行测试,并查看结果。
测试已经通过。默认情况下,TestCafe在控制台中显示测试结果。还有一些插件可以为TeamCity、Slack等提供定制的记者。
测试失败定位错误
让我们看看失败的测试是什么样子的。更改最后一个断言,以使测试失败:
1.expect(findBookPage.bookPreviews.count).eql(0);
现在运行测试。
对于每个失败的测试,TestCafe会突出显示失败的步骤。它还报告浏览器、CallSite和其他详细信息,使您能够快速找到故障原因。要修复测试,请将代码改回。
下一步:有用的链接
在本教程中,我们使用TestCafe和页面模型模式进行了简单的测试。有关更多示例和建议,请查看官方documentation.
您可以在GitHub上找到此tutorial](https://github.com/miherlosev/e2e_angular2)的完整[代码。
如果您对TestCafe有任何疑问,请随时在论坛上提问。有关功能请求和错误,请转到GitHub问题页面。