内容简介:在上一期内容中,我们介绍了Node.js 结合 Cucumber开发多端的自动化,并发布了演示视频:这一期我们就主要介绍一下开发的详细步骤。通过本例学习,可以掌握如何同时测试Windows,Mobile,Web应用。
在上一期内容中,我们介绍了Node.js 结合 Cucumber开发多端的自动化,并发布了演示视频: 如何做跨平台业务流程自动化(Windows,Moible,Web)
这一期我们就主要介绍一下开发的详细步骤。
主要目标
通过本例学习,可以掌握如何同时测试Windows,Mobile,Web应用。
环境配置
- 操作系统:Windows 10
- 开发语言:node.js
- 编辑工具:CukeTest
- 被测应用:Windows Outlook邮件
- 桌面端:Windows 10 Mail
Mobile端:安卓应用商店搜索"Outlook" Web端: outlook.live.com/mail/
其它必要库
自动化Mobile端使用到Appium,请在本机安装好Appium 以及Android相关配置环境(具体请在网上查找相关资料)
自动化Web端需要使用到selenium-server,以及浏览器驱动,可以在npm.taobao.org/ 下载。
操作步骤
1. 创建项目
打开CukeTest,新建项目,【项目模板】选择 "Basic",【项目名】输入"OutlookTesting",【项目路径】输入自己的本地目录。点击【创建】
2. 安装项目依赖库
CukeTest本身自带的有操作Windows库的控件,所以我们只需要引用即可。针对Web端和Mobile端,我们使用开源的webdriver.io库作为实现库。具体api可以参考官方文档 webdriver.io/guide.html
【项目名】右键--【在命令行窗口打开】
在打开的命令行窗口输入
npm install webdriverio @types/webdriverio --save 复制代码
安装成功之后会有如下提示
npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN outlooktesting@1.0.0 No repository field. + @types/webdriverio@4.13.0 + webdriverio@4.14.0 added 155 packages from 192 contributors and audited 345 packages in 50.09s found 0 vulnerabilities 复制代码
3. 创建驱动
分别为Windows,Mobile,Web 创建不同驱动。
features/support/web_driver.js
var webdriverio = require('webdriverio');// 设置浏览器信息 var options = { desiredCapabilities: { browserName: 'chrome' }}; //创建driver function createDriver(){ return webdriverio.remote(options) } exports.driver = createDriver(); 复制代码
features/support/mobile_driver.js
const webdriverio = require('webdriverio'); //设置被测应用参数 let options = { desiredCapabilities: { platformName: "Android", deviceName: "9c83590", //设备序列串号 // platformVersion: "5.1", //系统平台版本 appPackage: "com.microsoft.office.outlook", //package 名字 appActivity: ".MainActivity", //启动activity 名字 resetKeyboard: true, noReset: true, unicodeKeyboard: true }, host: "127.0.0.1", port: 4723 } //根据参数配置创建WebDriverIO实例; function createDriver() { const client = webdriverio.remote(options); return client; } exports.app_driver = createDriver(); 复制代码
创建Windows桌面应用模型文件features\support\Mail.tmodel
Windows驱动文件 features\support\win_driver.js
const { TestModel, Auto } = require("leanpro.win"); let path = require('path') let tmodelfile = path.join(__dirname,'Mail.tmodel') var model = TestModel.loadModel(tmodelfile); exports.model = model; 复制代码
此时的目录结构为
│ package-lock.json │ package.json │ └─features │ feature1.feature │ ├─step_definitions │ definitions1.js │ └─support Mail.tmodel mobile_driver.js web_driver.js win_driver.js 复制代码
4. 编写测试场景
为了让我们的测试更有趣,这里虚拟了一个这样的剧本:
Jason作为公司的老板,开会需要用到一些培训文档,Carol作为公司职员,已经将培训文档做好并存储在公司电脑上。 Jason使用Windows桌面端 Mail向Carol发送邮件索要文档。 Carol在公司外边使用mobile收到邮件后利用mobile端回复Jason稍后会回到公司发送。 Carol回到公司后使用web端将文档作为附件发送给Jason。
利用这样的用户场景:可以将Windows,Mobile,Web 这三端的自动化连接起来。于是我们在feature文件中可以如下编辑:
features/feature1.feature
# language: zh-CN 功能: 同时自动化desktop,mobile,web Outlook 应用分为移动端,Windows端和Web端。 Jason在Windows端给Carol发送邮件,Carol手机端收到邮件后回复Jason。 之后Carol在Web端给Jason发送一封带有附件的邮件。 @desktop 场景: Jason使用PC端Mail给Carol发送邮件索要文档 假如打开Outlook桌面客户端 当点击新建邮件 并且在收件人,主题,收件内容中输入对应的信息 同时点击发送邮件 @mobile 场景: Carol手机端回复Jason稍后发送 假如打开手机端Outlook 当打开收件箱窗口 同时打开未读邮件 同时答复框内回复对应内容并发送 @web 场景: Carol使用web端回复Jason 假如用户Carol登录Outlook web页面 当打开收件箱并打开最新一次的邮件 那么回复Jason邮件并上传相关文档 复制代码
对应的可视化界面
5. 完善自动化脚本
在features/step_definitons 目录里分别创建mobileAction.js, webAction.js, windowsAction.js 分别存储3端的自动化脚本。
场景1中,实现对Windows桌面应用的自动化。
- 打开windowsAction.js, 点击场景1中每个操作步骤后的灰色按钮,在windwosAction.js中生成Windows应用操作的自动化代码样例。
var { Given, When, Then } = require('cucumber') Given(/^打开Outlook桌面客户端$/, async function () { return 'pending'; }); When(/^点击新建邮件$/, async function () { return 'pending'; }); When(/^在收件人,主题,收件内容中输入对应的信息$/, async function () { return 'pending'; }); When(/^点击发送邮件$/, async function () { return 'pending'; }); 复制代码
- 打开tmodel文件,利用CukeTest自带的模型管理器选取Windows操作控件,具体选取方式可以参考视频教程 ke.qq.com/course/3473…
- Windows对象选取完成后实现windowsAction.js中定义的操作步骤。
引入Windows对象模型
const { model } = require('../support/win_driver'); const { Util } = require('leanpro.common'); 复制代码
- 模型管理器中根据选取的对象控件复制对应的实现代码。最终代码 features/step_definitons/windowsAction.js
const { Given, When, Then } = require('cucumber'); const { model } = require('../support/win_driver'); const { Util } = require('leanpro.common'); Given(/^打开Outlook桌面客户端$/, async function () { await model.getButton("开始").click(0, 0, 1); await Util.delay(2000); await model.getListItem("Mail").click(0, 0, 1); }); When(/^点击新建邮件$/, async function () { await Util.delay(2000) await model.getButton("New mail").click(0, 0, 1); }) ;When(/^在收件人,主题,收件内容中输入对应的信息$/, async function () { await model.getEdit("To:").clearAll(); await model.getEdit("To:").set("carolseaver1@outlook.com"); await model.getEdit("Subject").clearAll(); await model.getEdit("Subject").set("培训大纲"); let content = "hi,Carol:{ENTER}请把本次培训内容发送给我好吗?{ENTER}Jason Seaver" await model.getDocument("消息").clearAll(); await model.getDocument("消息").set(content);}); When(/^点击发送邮件$/, async function () { await model.getButton("Send").click(0, 0, 1); await Util.delay(1000) }); 复制代码
场景2中,实现对Mobile应用的自动化。
- 打开features/step_definitions/moblieAction.js, 点击feature的可视化界面中场景2中每个步骤后的灰色按钮,在mobileAction.js中生成手机端操作代码样例。
var { Given, When, Then } = require('cucumber') Given(/^打开手机端Outlook$/, async function () { return 'pending'; }); When(/^打开收件箱窗口$/, async function () { return 'pending'; }) ;When(/^打开未读邮件$/, async function () { return 'pending'; }); When(/^答复框内回复对应内容并发送$/, async function () { return 'pending'; }); 复制代码
- 引入手机端驱动
const { Util} = require('leanpro.common') const { app_driver } = require('../support/mobile_driver') 复制代码
使用webdriver.io 相关api 实现自动化操作代码。最终代码 features/step_defintions/mobileAction.js
const { Given, When, Then } = require('cucumber') const { Util} = require('leanpro.common') const { app_driver } = require('../support/mobile_driver') Given(/^打开手机端Outlook$/, async function () { // 手机客户端接收邮件需要5-10秒的延迟 await Util.delay(5000) return true }); When(/^打开收件箱窗口$/, async function () { await app_driver.click('~打开导航抽屉'); await app_driver.click('android=new UiSelector().resourceId("com.microsoft.office.outlook:id/drawer_item_title").index(1).text("收件箱")')}); Then(/^打开未读邮件$/, async function () { await app_driver.waitForExist('android=new UiSelector().resourceId("com.microsoft.office.outlook:id/message_snippet_frontview").index(0).className("android.widget.LinearLayout")',20*1000); await app_driver.click('android=new UiSelector().resourceId("com.microsoft.office.outlook:id/message_snippet_frontview").index(0).className("android.widget.LinearLayout")')});Then(/^答复框内回复对应内容并发送$/, async function () { let loctor = 'new UiSelector().text("答复").index(1)' await app_driver.click('android='+loctor) await app_driver.clearElement('~邮件正文。') let content = ` Hi,Jason: 培训内容文档在我公司PC上保存,现在我在外边,稍后我到公司回复您。 Carol Seaver ` await app_driver.setValue('~邮件正文。',content); await app_driver.click('~发送'); }); 复制代码
场景3中,实现对web应用的自动化。
- 打开features/step_definitions/webAction.js, 点击feature的可视化界面中场景3中每个步骤后的灰色按钮,在mobileAction.js中生成web端操作代码样例。
var { Given, When, Then } = require('cucumber') Given(/^用户Carol登录Outlook web页面$/, async function () { return 'pending'; }); When(/^打开收件箱并打开最新一次的邮件$/, async function () { return 'pending'; }); Then(/^回复Jason邮件并上传相关文档$/, async function () { return 'pending'; }); 复制代码
- 添加对web_driver 的引用:
const { driver } = require('../support/web_driver'); 复制代码
- 根据webdriverio相关api 完成对应的操作。最终代码 features/step_definitions/webAction.js
var { Given, When, Then } = require('cucumber') const { Auto } = require('leanpro.win') const { Util } = require('leanpro.common') const path = require('path') const assert = require('assert'); const { driver } = require('../support/web_driver'); Given(/^用户Carol登录Outlook web页面$/, async function () { await driver.url("https://outlook.live.com/mail/inbox#"); await driver.click('div.headerHero>a:last-child'); await driver.setValue('#i0116','carolseaver1@outlook.com'); await driver.click('#idSIButton9'); await driver.setValue('#i0118','密码'); await driver.waitForEnabled('#idSIButton9',20*1000) await driver.click('#idSIButton9'); }); When(/^打开收件箱并打开最新一次的邮件$/, async function () { await driver.waitForEnabled('div > div[role="option"] > div[draggable="true"] > div[tabindex="-1"]',15*1000); await driver.click('div > div[role="option"] > div[draggable="true"] > div[tabindex="-1"]' ); }); Then(/^回复Jason邮件并上传相关文档$/,{timeout:180*1000}, async function () { // await Util.delay(1000); let content = ` hi,Jason: 附件为本次培训内容大纲。请查收,谢谢。 Carol Seaver ` let bool = await driver.waitForEnabled('div[tabindex="-1"] > div > button[type="button"]> div >span',45*1000) console.log("ele",bool); if(bool==true){ console.log("get Text ===") let text = await driver.getText('div[tabindex="-1"] > div > button[type="button"]> div >span') console.log("text == ",text) if(text.includes('加载')){ console.log('click loading') await driver.click('div[tabindex="-1"] > div > button[type="button"]> div >span'); await Util.delay(1000) await driver.click('div[tabindex="-1"] > div > button[type="button"]> div >span') }else{ await driver.click('div[tabindex="-1"] > div > button[type="button"]> div >span') } } // await driver.keys() // let input_area = await driver.element('div[dir="ltr"]'); await driver.waitForExist('div[dir="ltr"]',20*1000); await driver.click('div[dir="ltr"]') // await driver.clearElement('div[dir="ltr"]') await driver.setValue('div[dir="ltr"]',content); await driver.waitForEnabled('button[name="附加"]',20*1000); await driver.click('button[name="附加"]'); await Util.delay(500) await driver.waitForVisible('button[name="浏览此计算机"]',20*1000); await driver.click('button[name="浏览此计算机"]'); await Util.delay(1500); let uplaodFilePane =await Auto.getPane({ "className": "Chrome_WidgetWin_1", "name": "邮件 - Carol Seaver - Outlook - Google Chrome" }).getWindow({ "className": "#32770", "title": "打开" }).getComboBox({ "automationId": "1148", "name": "文件名(N):" }).getEdit({ "automationId": "1148", "name": "文件名(N):" }) let filepath = path.join(__dirname, '..', '..', 'files','培训大纲.md') await uplaodFilePane.clearAll(); await uplaodFilePane.set(filepath); await Auto.getPane({ "className": "Chrome_WidgetWin_1", "name": "邮件 - Carol Seaver - Outlook - Google Chrome" }).getWindow({ "className": "#32770", "title": "打开" }).getButton({ "automationId": "1", "name": "打开(O)" }).click(0, 0, 1); await driver.waitForEnabled('div.ms-Button-flexContainer> i[data-icon-name="Send"]',20*1000); await driver.click('div.ms-Button-flexContainer> i[data-icon-name="Send"]') await Util.delay(500); }); 复制代码
设置hooks
- 打开features/support/hooks.js 文件,设置运行时:
const { BeforeAll, After, Before, AfterAll, setDefaultTimeout } = require('cucumber'); const cuketest = require('cuketest'); const { Util } = require('leanpro.win'); const { app_driver } = require('./mobile_driver'); const { model } = require('./win_driver'); const { driver } = require('./web_driver'); //set default step timeout setDefaultTimeout(120 * 1000); BeforeAll(async function () { await cuketest.minimize(); await driver.init(); await driver.windowHandleMaximize(); await driver.timeouts(30 * 1000); await app_driver.init(); await app_driver.timeouts(10000); }) After({ tags: "@desktop" }, async function () { let screenshot = await model.getWindow("Window").takeScreenshot(); this.attach(screenshot, 'image/png'); await cuketest.delay(1000); await model.getWindow("Window").close(); }); After({ tags: "@mobile" }, async function () { let screenshot = await app_driver.saveScreenshot(); this.attach(screenshot, 'image/png'); await app_driver.end();}); After({ tags: "@web" }, async function () { let screenshot = await driver.saveScreenshot(); this.attach(screenshot, 'image/png'); //clean up cookies await driver.deleteCookie(); await driver.end(); }); AfterAll(async function () { await cuketest.restore();}) 复制代码
运行
- 启动appium
命令行中输入 appium
$ appium [Appium] Welcome to Appium v1.9.1 [Appium] Appium REST http interface listener started on 0.0.0.0:4723 复制代码
- 启动selenium-server
$ java -jar -Dwebdriver.chrome.driver=./chromedriver.exe selenium-server-standalone-3.141.0.jar14:53:29.932 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.0, revision: 2ecb7d9a14:53:30.135 INFO [GridLauncherV3.lambda$buildLaunchers$3] - Launching a standalone Selenium Server on port 44442018-11-08 14:53:30.260:INFO::main: Logging initialized @1318ms to org.seleniumhq.jetty9.util.log.StdErrLog14:53:30.728 INFO [WebDriverServlet.<init>] - Initialising WebDriverServlet14:53:31.564 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 4444 复制代码
点击feature文件上每个场景后的运行按钮可以运行单个场景,点击运行项目,可以运行整个项目。运行项目后会生成对应的图文报表。
总结
依托强大的Node.js生态和大量的开源库。使用CukeTest配合开源的 工具 可以完成各类应用的自动化。既快速又免费,有兴趣的可以尝试一下噢。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
RESTful Web Services Cookbook
Subbu Allamaraju / Yahoo Press / 2010-3-11 / USD 39.99
While the REST design philosophy has captured the imagination of web and enterprise developers alike, using this approach to develop real web services is no picnic. This cookbook includes more than 10......一起来看看 《RESTful Web Services Cookbook》 这本书的介绍吧!