内容简介:在上一期内容中,我们介绍了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配合开源的 工具 可以完成各类应用的自动化。既快速又免费,有兴趣的可以尝试一下噢。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Essential C++中文版
李普曼 (Stanley B.Lippman) / 侯捷 / 电子工业出版社 / 2013-8-1 / CNY 65.00
本书以四个面向来表现C++的本质:procedural(面向过程的)、generic(泛型的)、object-based(基于对象的)、objectoriented(面向对象的)。全书围绕一系列逐渐繁复的程序问题,以及用以解决这些问题的语言特性来组织。循此方式,你将不只学到C++的功能和结构,也可学到它们的设计目的和基本原理。 本书适合那些已经开始从事软件设计,又抽不出太多时间学习新技术的程......一起来看看 《Essential C++中文版》 这本书的介绍吧!