端到端集成测试与页面对象的应用
在软件开发中,端到端测试是确保整个系统功能正常运行的重要环节。本文将详细介绍如何使用 Protractor 进行端到端测试,以及如何利用页面对象来优化测试代码。
1. 删除任务测试
在任务管理系统中,删除任务是一个常见的操作。为了测试删除任务的功能,我们需要点击任务表格中任务旁边的删除链接。由于可能存在多个删除链接,我们选择第二个链接(索引为 1),以确保删除第二个任务。
以下是具体的测试代码:
it('should successfully delete a task', function() {
element.all(by.linkText('delete')).get(1).click();
expect(element(by.id('message')).getText())
.to.eventually.contain('task deleted');
expect(element(by.id('tasks')).getText())
.to.eventually.not.contain('Test Routes');
});
在这段代码中,我们使用了 Protractor 的 API 来定位和操作页面元素。
element.all(by.linkText('delete')).get(1)
用于选择第二个删除链接,然后调用
click()
方法进行点击操作。接着,我们使用
expect
语句来验证删除操作是否成功。
2. 集成测试问题与页面对象的引入
在编写集成测试时,我们可能会遇到一些问题,例如代码噪音、重复代码、页面实现的紧密耦合以及对 API 的紧密依赖。为了解决这些问题,我们可以使用页面对象。
页面对象是一种设计模式,它将页面的内容抽象成一个对象,测试代码可以通过这个对象来与页面进行交互,而不是直接操作浏览器对象和页面元素。这样可以减少代码噪音、重复代码,并且降低耦合度。
以下是一个使用页面对象的示例:
// 直接使用 Protractor API 的测试代码
it('should successfully add a task', function() {
element(by.id('name')).sendKeys('Create Quality Code');
element(by.id('date')).sendKeys('12/15/2016');
element(by.id('submit')).click();
expect(element(by.id('message')).getText())
.to.eventually.contain('task added');
expect(element(by.id('tasks')).getText())
.to.eventually.contain('Create Quality Code');
});
// 使用页面对象的测试代码
it('should successfully add a task', function() {
page.name = 'Create Quality Code';
page.date = '12/15/2016';
page.submit();
eventually(page.message).contain('task added');
eventually(page.tasksAsText).contain('Create Quality Code');
});
可以看到,使用页面对象的测试代码更加简洁、易读,并且减少了对 Protractor API 的直接依赖。
3. 创建页面对象
为了创建页面对象,我们需要两个文件:一个页面类和一个便利函数。
以下是
tasksj.html
页面的页面对象代码:
// tasksj-page.js
var fetchById = function(id) {
return element(by.id(id));
};
var sendKey = function(element, text) {
element.sendKeys(text);
};
var TasksJPage = function() {
browser.get('/tasksj.html');
};
TasksJPage.prototype = {
get tasksCount() { return
fetchById('taskscount').getText(); },
get tasksAsText() { return fetchById('tasks').getText(); },
get message() { return fetchById('message').getText(); },
deleteAt: function(index) {
return element.all(by.linkText('delete')).get(index);
},
set name(text) { sendKey(fetchById('name'), text); },
set date(text) { sendKey(fetchById('date'), text); },
submit: function() { fetchById('submit').click(); }
};
module.exports = TasksJPage;
在这个页面对象中,我们定义了一些属性和方法,用于获取页面元素的文本内容、执行点击操作等。
4. 便利函数的使用
为了进一步减少测试代码的噪音,我们可以使用一个便利函数来简化
expect...eventually
的使用。
以下是便利函数的代码:
// eventually.js
var expect = require('chai').expect;
require('chai').use(require('chai-as-promised'));
module.exports = function(object) {
return expect(object).to.be.eventually;
};
使用这个便利函数,我们可以将测试代码中的
expect...eventually
替换为
eventually
,使代码更加简洁。
5. 重写测试代码
使用页面对象和便利函数,我们可以重写之前的测试代码。以下是重写后的测试代码:
// tasksj-usepage-test.js
var eventually = require('./eventually');
var TasksPage = require('./tasksj-page');
describe('tasks ui test', function() {
var page;
beforeEach(function() {
browser.ignoreSynchronization = true;
page = new TasksPage();
});
afterEach(function() {
browser.ignoreSynchronization = false;
});
it('page should show correct task count', function() {
eventually(page.tasksCount).eql('4');
});
it('page should display tasks', function() {
eventually(page.tasksAsText).contain('Test Models');
eventually(page.tasksAsText).contain('Test UI');
});
it('should successfully add a task', function() {
page.name = 'Create Quality Code';
page.date = '12/15/2016';
page.submit();
eventually(page.message).contain('task added');
eventually(page.tasksAsText).contain('Create Quality Code');
});
it('should successfully delete a task', function() {
page.deleteAt(1).click();
eventually(page.message).contain('task deleted');
eventually(page.tasksAsText).not.contain('Test Routes');
});
});
通过使用页面对象和便利函数,测试代码变得更加简洁、易读,并且减少了对 Protractor API 的直接依赖。
6. AngularJS 页面测试
Protractor 为 AngularJS 应用提供了一些特殊的功能。在测试 AngularJS 页面时,由于页面通常使用
ng-model
、
ng-repeat
等指令,我们可以使用 Protractor 提供的便利函数来定位元素。
以下是一些常用的 Protractor 函数及其对应的 AngularJS 指令:
| 调用获取定位器 | 示例 | AngularJS 指令 |
| — | — | — |
| by.model(‘…’) | ng-model=‘…’ | ng-model |
| by.binding(‘controller.message’) | {{ controller.message }} | bindings |
| by.css(‘[ng-click=”…”]’) | ng-click=‘…’ | ng-click |
| by.repeater(‘task in…’) | ng-repeat=”task in…” | ng-repeat |
| by.repeater(‘task in…’).row(index) | ng-repeat=”task in…” | ng-repeat—a row |
| by.repeater(‘task in…’).column(‘task.name’) | ng-repeat=”…” | ng-repeat—columns |
为了测试 AngularJS 页面,我们需要创建一个专门的页面对象。以下是
tasksa.html
页面的页面对象代码:
// tasksa-page.js
var fetchByModel = function(model) {
return element(by.model(model));
};
var fetchByBinding = function(binding) {
return element(by.binding(binding));
};
var fetchByNgClick = function(clickFunction) {
return element(by.css('[data-ng-click="' + clickFunction + '"]'));
};
var sendKey = function(element, text) {
element.sendKeys(text);
};
var TasksAPage = function() {
browser.get('/tasksa.html');
};
TasksAPage.prototype = {
get tasksCount() {
return
fetchByBinding('controller.tasks.length').getText();
},
get tasksAsText() {
return element.all(by.repeater('task in controller.tasks')
.column('task.name')).getText();
},
get message() { return fetchByBinding('controller.message').getText(); },
deleteAt: function(index) {
return element(by.repeater('task in controller.tasks').row(index))
.element(by.tagName('A'));
},
set name(text) { sendKey(fetchByModel('controller.newTask.name'), text); },
set date(text) { sendKey(fetchByModel('controller.newTask.date'), text); },
submit: function() {
fetchByNgClick('controller.addTask();').click();
},
get submitDisabled() {
return fetchByNgClick('controller.addTask();').getAttribute('disabled');
}
};
module.exports = TasksAPage;
使用这个页面对象,我们可以编写测试代码来验证
tasksa.html
页面的功能。
7. Angular 2 页面测试
在测试 Angular 2 页面时,虽然 Protractor 对 Angular 2 的支持还在开发中,但我们仍然可以使用 Protractor 进行测试。
首先,我们需要在
protractor.conf.js
文件中添加一个新的配置项:
// protractor.conf.js
var app = require('./app');
var config = require('./config.json');
exports.config = {
directConnect: true,
baseUrl: 'http://localhost:' + (process.env.PORT || 3030),
useAllAngular2AppRoots: true,
framework: 'mocha',
mochaOpts: {
reporter: 'dot',
timeout: 10000,
},
specs: ['test/integration/*.js'],
};
然后,我们需要创建一个专门的页面对象来测试 Angular 2 页面。以下是
tasksa.html
页面的 Angular 2 版本的页面对象代码:
// tasksa-page.js
var fetchModelById = function(modelId) {
return element(by.id(modelId));
};
var fetchBindingById = function(bindingID) {
return element(by.id(bindingID));
};
var fetchClickById = function(clickId) {
return element(by.id(clickId));
};
var sendKey = function(element, text) {
text.split('').forEach(function(ch) {
element.sendKeys(ch);
});
};
var TasksAPage = function() {
browser.get('/');
};
TasksAPage.prototype = {
get tasksCount() {
return fetchBindingById('length').getText();
},
get tasksAsText() {
return element(by.css('table')).getText();
},
get message() { return fetchBindingById('message').getText(); },
deleteAt: function(index) {
return element.all(by.css('table tr')).get(index)
.element(by.tagName('A'));
},
set name(text) { sendKey(fetchModelById('name'), text); },
set date(text) {
var textSplit = text.split('/');
var dateElement = fetchModelById('date');
sendKey(dateElement, textSplit[0]);
sendKey(dateElement, '/' + textSplit[1]);
sendKey(dateElement, '/' + textSplit[2]);
},
submit: function() {
fetchClickById('submit').click();
},
get submitDisabled() {
return fetchClickById('submit').getAttribute('disabled');
}
};
module.exports = TasksAPage;
最后,我们可以按照以下步骤进行 Angular 2 页面的集成测试:
1. 运行
npm install
安装依赖。
2. 运行
npm run-script update-driver
更新 WebDriver。
3. 启动数据库守护进程:
mongod --dbpath db
。
4. 运行
npm run-script test-integration
启动 Protractor 进行测试。
通过以上步骤,我们可以使用 Protractor 对 Angular 2 页面进行有效的端到端测试。
综上所述,使用页面对象和 Protractor 可以帮助我们编写更加简洁、易读、可维护的端到端测试代码。无论是 jQuery 页面、AngularJS 页面还是 Angular 2 页面,我们都可以通过合理的设计和使用页面对象来提高测试效率和质量。
端到端集成测试与页面对象的应用
8. 测试代码示例及分析
为了更清晰地展示不同页面测试代码的差异和优势,我们将之前的测试代码进行对比分析。
以下是
tasksj.html
页面使用页面对象前后的测试代码对比:
// 直接使用 Protractor API 的测试代码
it('should successfully add a task', function() {
element(by.id('name')).sendKeys('Create Quality Code');
element(by.id('date')).sendKeys('12/15/2016');
element(by.id('submit')).click();
expect(element(by.id('message')).getText())
.to.eventually.contain('task added');
expect(element(by.id('tasks')).getText())
.to.eventually.contain('Create Quality Code');
});
// 使用页面对象的测试代码
it('should successfully add a task', function() {
page.name = 'Create Quality Code';
page.date = '12/15/2016';
page.submit();
eventually(page.message).contain('task added');
eventually(page.tasksAsText).contain('Create Quality Code');
});
从上述代码可以看出,使用页面对象后,测试代码更加简洁,减少了对
element
和
by
等 Protractor API 的直接调用,降低了代码的耦合度。
接下来是
tasksa.html
页面的测试代码:
// tasksa-test.js
var eventually = require('./eventually');
var TasksPage = require('./tasksa-page');
describe('tasks ui test', function() {
var page;
beforeEach(function() {
page = new TasksPage();
});
it('page should show correct task count', function() {
eventually(page.tasksCount).eql('4');
});
it('page should display tasks', function() {
eventually(page.tasksAsText).contain('Test Models');
eventually(page.tasksAsText).contain('Test UI');
});
it('should successfully add a task', function() {
page.name = 'Create Quality Code';
page.date = '12/15/2016';
page.submit();
eventually(page.message).contain('task added');
eventually(page.tasksAsText).contain('Create Quality Code');
});
it('should successfully delete a task', function() {
page.deleteAt(1).click();
eventually(page.message).contain('task deleted');
eventually(page.tasksAsText).not.contain('Test Routes');
});
it('should disable submit button on page load', function() {
eventually(page.submitDisabled).eql('true');
});
it('should enable submit button on data entry', function() {
page.name = 'Create Quality Code';
page.date = '12/15/2016';
eventually(page.submitDisabled).not.eql('true');
});
});
在
tasksa.html
页面的测试中,我们不仅验证了任务的添加和删除功能,还增加了对提交按钮状态的验证,确保在页面加载时按钮是禁用的,输入数据后按钮变为可用状态。
9. 测试流程总结
为了更直观地展示整个测试流程,我们可以使用 mermaid 流程图来表示:
graph LR
A[开始] --> B[选择测试页面类型]
B --> C{jQuery 页面}
B --> D{AngularJS 页面}
B --> E{Angular 2 页面}
C --> F[创建 tasksj 页面对象]
D --> G[创建 tasksa 页面对象(AngularJS 版本)]
E --> H[创建 tasksa 页面对象(Angular 2 版本)]
F --> I[编写测试代码并执行]
G --> I
H --> I
I --> J[验证测试结果]
J --> K[结束]
从流程图可以看出,无论选择哪种页面类型,都需要先创建对应的页面对象,然后编写测试代码并执行,最后验证测试结果。
10. 关键要点回顾
在进行端到端集成测试时,我们需要关注以下几个关键要点:
-
页面对象的使用
:通过创建页面对象,可以将页面的操作和元素定位封装起来,减少测试代码的噪音和重复,提高代码的可维护性。
-
Protractor API 的选择
:根据页面的特点和使用的框架,选择合适的 Protractor API 来定位元素。对于 AngularJS 页面,可以使用
by.model
、
by.binding
等特殊函数;对于普通页面,可以使用
by.id
、
by.css
等函数。
-
测试流程的规范
:遵循一定的测试流程,包括创建页面对象、编写测试代码、执行测试和验证结果,确保测试的准确性和可靠性。
11. 常见问题及解决方案
在测试过程中,可能会遇到一些常见问题,以下是一些问题及对应的解决方案:
| 常见问题 | 解决方案 |
| — | — |
| 元素定位失败 | 检查元素的选择器是否正确,确保元素在页面上存在且可见。对于 AngularJS 页面,使用合适的 AngularJS 特定选择器。 |
| 测试结果不稳定 | 检查页面加载时间和异步操作,确保测试代码在页面完全加载后执行。可以使用
browser.wait
等函数来等待元素的出现。 |
| 代码耦合度高 | 使用页面对象将页面操作和元素定位封装起来,减少测试代码对页面实现的依赖。 |
12. 总结与展望
通过本文的介绍,我们了解了如何使用 Protractor 进行端到端集成测试,以及如何利用页面对象来优化测试代码。页面对象的使用可以使测试代码更加简洁、易读和可维护,同时降低了代码的耦合度。
在未来的测试工作中,我们可以进一步探索更多的测试技巧和工具,不断提高测试效率和质量。例如,可以结合自动化测试框架,实现更复杂的测试场景;可以使用数据驱动测试,提高测试的覆盖率。总之,持续学习和实践是提高测试能力的关键。
通过合理运用页面对象和 Protractor 进行端到端集成测试,我们能够更好地保证软件的质量和稳定性,为用户提供更优质的产品体验。
超级会员免费看
8840

被折叠的 条评论
为什么被折叠?



