如果您正在寻找一个简单的测试框架做自动化测试,您可以考虑用于使用行为驱动框架 (BDD),具体技术使用流行的 JavaScript,Selenium 和 Cucumber 的组合。它们作为一些流行的自动化技术,可以帮你完成 Web 应用自动化测试的任务。下面将详细介绍如何使用 JavaScript,Selenium 和 Cucumber 实现自动化测试套件。
当试图在项目中利用 BDD 构建自动化测试套件时,有人会对如何具体实现陷入迷茫,就好像这是一项艰巨的任务 - 事实并非如此。
希望本文能够在一定程度上揭开这个过程的神秘面纱,并提供一些有用的技巧来帮助您开始您的项目。
BDD(行为驱动开发)是一组开发实践,旨在减少由常见的开发问题引起的错误 - 例如由于需求被误解或需求阐述不清而不得不重新开始工作。
BDD 是 TDD 的扩展, 主要差异在于 BDD 指定了测试用例。
BDD 使用清晰,通俗易懂的自然语言来测试案例,这些测试案例来自 “从外到内” - 即直接来自业务需求和应用程序行为的期望业务成果。
这些测试用例中使用的明确语言通常以 Gherkin 这种域特定语言形式编写。
每一个 web 应用几乎都需要有注册,登陆功能,我们以基本登陆用例为主。首先如下是 Gherkin 中的描述。
# language: zh-CN
功能: Web应用程序的登录页面
背景:用户未登陆
假如我浏览到Web应用程序而我尚未登录
场景:显示的登录页面
假如浏览到网站首页
当点击登录按钮应该跳转到登录页面
场景:我尝试使用无效用户登录
当我使用无效用户登录
同时我看到"用户名或密码不正确"错误消息
场景:我尝试使用有效用户登录
当我使用有效用户登录
同时我看到一个"首页"页面
以这种形式编写测试用例的真正好处有两个:
它们作为项目的文档,任何人都可以阅读和理解。
他们清楚地阐明了我们需要代码做什么。
步骤定义 (Step Definition) 将 Gherkin 中 feature 文件定义的步骤 (step) 映射到具体操作代码。我们已经在 feature 文件中记录了我们希望应用程序期望的行为,现在我们需要编写代码测试该行为。
使用 Cucumber,我们可以用以下形式编写空白步骤定义 - 其中注释和正则表达式将特征文件中的步骤映射到具体操作:
var { Given, When, Then } = require('cucumber')
Given(/^我浏览到Web应用程序而我尚未登录$/, async function () {
return 'pending';
});
Given(/^浏览到网站首页$/, async function () {
return 'pending';
});
When(/^点击登录按钮应该跳转到登录页面$/, async function () {
return 'pending';
});
When(/^我使用无效用户登录$/, async function () {
return 'pending';
});
When(/^我看到"([^"]*)"错误消息$/, async function (arg1) {
return 'pending';
});
When(/^我使用有效用户登录$/, async function () {
return 'pending';
});
When(/^我看到一个"([^"]*)"页面$/, async function (arg1) {
return 'pending';
});
我们希望能够运行我们的脚本测试实际运行的 Web 应用程序。为此,我们可以在 Cucumber 步骤定义中编写 Selenium 自动化库的代码。
Selenium 是一个浏览器自动化框架,您可以利用它的 WebDriver 对象操作真实浏览器和网页,并使用 CSS 或 XPath 选择器在屏幕上定位元素。WebDriver 针对各种不同的浏览器有不同的实现,它还能运行没有 GUI 的浏览器,它也叫无头 (headless) 浏览器。
下面的 web_driver.js 文件中实现的代码创建了 WebDriver 的实例,以供后继调用。
// 引入 Chrome驱动
require('chromedriver');
// 引入Selenium-Webdriver
const webDriver = require('selenium-webdriver');
//根据您的浏览器配置创建WebDriver实例;
function createDriver() {
let browserConfig = process.env.BROWSER || 'chrome';
let browser = browserConfig.toLowerCase();
if (['chrome', 'firefox', 'ie'].indexOf(browser) < 0) browser = 'chrome'; //default to chrome
return new webDriver.Builder().forBrowser(browser).build();
}
exports.driver = createDriver();
页面对象模型 (Page Object,或 PO) 是一种设计模式,它将页面上的页面和元素表示为面向对象的类。当您需要与屏幕上的元素交互时,您不直接调用 WebDriver。而是在类上调用方法表示页面上的元素。
让我们以登录页面应用为例,编写如下的 login_page.js 文件:
let {driver} = require('./support/web_driver')
class LoginPage {
constructor(){
this.Base_Url = "http://localhost";
this.Login_Css_selector='#login';
this.UserName_Css_selector="#name";
this.PassWord_Css_selector="#pass";
this.Login_Btn_Css_selector="#login";
this.error_msg_Css_selector=".error";
this.index_Csss_selector='.index';
}
async go_Home() {
await driver.get(this.Base_Url)
}
async go_LoginPage(){
await driver.findElement({css:this.Login_Css_selector}).click();
}
async set_UserName(username){
await driver.findElement({css:this.UserName_Css_selector}).sendKeys(username);
}
async set_PassWord(pass){
await driver.findElement({css:this.PassWord_Css_selector}).sendKeys(pass)
}
async click_Login_btn(){
await driver.findElement({css:this.Login_Btn_Css_selector}).click();
}
async action_login(username,passwd){
await this.set_UserName(username);
await this.set_PassWord(passwd);
await this.click_Login_btn();
}
}
module.exports = new LoginPage();
上面的类表示我们的登录页面 - 它定义了页面上的主要元素,并且实现了常用的操作。
根据上面定义的 PO 模型,在步骤定义 (definition.js) 中实现 feature 的代码:
var { Given, When, Then } = require('cucumber')
const assert = require('assert');
const LoginPage = require('./login_page')
const {driver} = require('./support/web_driver')
Given(/^我浏览到Web应用程序而我尚未登录$/, async function () {
await LoginPage.go_Home()
});
Given(/^浏览到网站首页$/, async function () {
await LoginPage.go_Home()
});
When(/^点击登录按钮应该跳转到登录页面$/, async function () {
await LoginPage.click_Login_btn()
let page_url = await driver.getCurrentUrl()
assert.equal(page_url,'http://localhost/login')
});
When(/^我使用无效用户登录$/, async function () {
await LoginPage.action_login('无效用户名','无效密码');
});
When(/^我看到"([^"]*)"错误消息$/, async function (msg) {
let text = await driver.findElement({css:LoginPage.error_msg_Css_selector}).getText();
assert.equal(text,msg)
});
When(/^我使用有效用户登录$/, async function () {
await LoginPage.action_login('user','pass')
});
When(/^我看到一个"([^"]*)"页面$/, async function (arg1) {
let text = await driver.findElement({ css: LoginPage.index_Css_selector }).getText();
assert.equal(text, msg)
});
在构建应用程序并添加更多功能时,会通过更多测试。为了确保测试通过,您只需要使用适当的选择器(在页面对象模型和元素中定义)选择元素,实现操作。
选用合适的测试工具会让你代码生成和调试事半功倍,例如在 CukeTest 中,可视化的显示了场景和相关步骤定义的关系并可即时跳转:
此外点击运行,也可快速得到报表: