在每次发布新版本之前、都需要回归核心功能、已确保上线后小程序也能按照预期运行. 目前这部分回归工作是由测试同事手工去验证测试用例、按照每周一版本的迭代节奏、回归就花了测试挺多时间的.
最近前端工作比较轻松、故在思考能否把这部分重复的工作交给程序自动来进行呢、省时省力。
小程序自动化SDK为开发者提供了一套通过外部脚本操控小程序的方案,从而实现小程序自动化测试的目的。
大白话翻译就是: 让开发者工具能按照代码的执行顺序自动在页面上完成点击、输入等操作(即模拟用户操作), 再将操作后的页面表现与预期想要的结果做对比得到测试结论(断言).
官网链接: developers.weixin.qq.com/miniprogram…
小程序自动化测试SDK: www.npmjs.com/package/min…
小程序自动化测试SDK具备的特性:
A. 控制小程序跳转到指定页面;
B. 获取小程序页面数据;
C. 获取小程序页面元素状态;
D. 触发小程序元素绑定事件;
E. 往AppService注入代码片段;
F. 调用wx对象上任意接口;
G. ...
A. 安装Node.js并且版本大于8.0;
B. 基础库版本为2.7.3及以上;
C. 开发者工具版本为1.02.1907232及以上;
D. 要做自动化测试的项目(后面会介绍哦);
如何查看&修改基础库版本、请见下图.

如何查看开发者工具版本、请见下图.
需要做自动化测试的项目(3选1):
A. 借助微信开发者工具新建个项目;
B. 开发者可直接从github下载小程序官方组件源码下来(小程序官方组件源码git地址: github.com/wechat-mini…);
C. 真实的项目;
在本篇文章中、为尽可能直白讲清楚实践步骤、采取方案A新建个项目(下文所有的演示都是基于该项目).
先来看看大概的一个目录结构如下: 若具备以上运行环境了、请继续往下看.
3.2 安装自动化测试SDK
Tips: cd 进入到项目根目录下, 终端执行如下命令。
npm i miniprogram-automator --save-dev
复制代码
带--save-dev跟不带的区别, 详细见下文: juejin.cn/post/684490…
该命令执行成功后、在终端会输出如下内容、说明安装成功.

终端也有些warn告警、暂不影响使用、故先不处理。
3.3 初体验
A. 开启工具安全设置中的CLI/HTTP调用功能。
具体操作指南如下: 打开微信开发者工具->设置-> 安全设置 -> 启用服务端口.

有关于CLI/HTTP功能的详细介绍、请戳此链接: developers.weixin.qq.com/miniprogram…
B. 先准备个需要做自动化测试的页面(为方便、下面会以最简单的demo来演示)
// 想要测试的页面index
// index.wxml
<view class="usermotto">
<text class="user-motto" bind:tap="tapFn">{{motto}}</text>
</view>
// index.js
// 获取应用实例
const app = getApp()
Page({
data: {
motto: 'Hello World',
},
tapFn (e) {
console.log(e,'测试自动化结果')
}
})
// index.wxss
.usermotto {
margin-top: 200px;
text-align: center;
}
复制代码
C. 在项目中新建文件、以.spec.js结尾(本文以在项目根目录下新建index.spce.js为例子), 输入类似如下内容(在实际过程中根据自己的项目修改即可)
const automator = require('miniprogram-automator')
automator.launch({
cliPath: '/Applications/wechatwebdevtools.app/Contents/MacOS/cli', // 工具 cli 位置,如果你没有更改过默认安装位置,可以忽略此项
projectPath: '/Users/susan.li/files/mini-demo', // 项目文件地址
}).then(async miniProgram => {
const page = await miniProgram.reLaunch('/pages/index/index')
await page.waitFor(500)
const element = await page.$('.user-motto')
console.log(await element.attribute('class'))
await element.tap()
await miniProgram.close()
})
复制代码
这里有3个点需要注意:
1.修改cli工具的路径(若您没更改过微信开发者工具的默认安装路径、可忽略此项、若改过安装路径的、就需要自行寻找安装路径是什么然后补充到这);
若不清楚如何查看软件安装位置的, 请戳此链接: juejin.cn/post/697060…
2.修改项目的文件路径(这里推荐绝对路径);
3.修改下面demo中实际想要操作的元素;
D.在终端输入如下命令、就在执行自动化测试结果了。
node index.spec.js
复制代码
我们来看看命令执行的全过程.
这里需要留意一点, 在自动化测试启动微信开发者工具时、留意右侧是否出现下图内容[启动自动化端口].

紧接着我们看看开发者工具是否有打印出相关内容 最后我们再看看终端, 你会发现终端会输出如下内容, 跟你代码的预期是符合的. 本演示demo想实现的是: 通过自动化测试SDK模拟用户点击Hello World文本(tap操作)、看能否正常执行bindtap里面的方法.
Question: 若在执行node index.spec.js中遇到如下报错, 是什么原因讷?
(node:2903) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'attribute' of null
at automator.launch.then (/xxx/index.spec.js:38:29)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:2903) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:2903) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
复制代码
至此、小程序自动化测试算是开了个头, 请接着往下看.
B. 执行如下命令全局安装Jest框架
npm i jest -g
复制代码
在安装过程中、遇到了如下报错:

根据报错提示、应该是权限不够, 故执行如下命令(实际过程开发者可根据自身报错去处理)
// 看看命令行、应该会提示你要输入密码。
sudo npm i jest -g
复制代码

C. 开启工具安全设置中的CLI/HTTP调用功能(同2.4打开步骤)
至此, Jest测试框架已搭建完成, 可进入到下一步.
4.2 Jest初体验
A. 编写脚本(目标: 自动启动微信开发者工具&&打开该项目)
const automator = require('miniprogram-automator')
describe('index', () => {
let miniProgram
let page
beforeAll(async () => {
miniProgram = await automator.launch({
projectPath: '/Users/susan.li/files/shop-mini',
cliPath: '/Applications/wechatwebdevtools.app/Contents/MacOS/cli'
})
page = await miniProgram.reLaunch('/pages/index/index')
await page.waitFor(5000)
}, 30000)
afterAll(async () => {
await miniProgram.close()
})
})
复制代码
B. 开启工具安全设置中的CLI/HTTP调用功能(同2.4打开步骤)
C. 关闭后、重新启动小程序到首页
D. 关闭工具安全设置中的CLI/HTTP调用功能(同2.4打开步骤)并关闭微信开发者工具
E. 在项目根目录下, 执行如下脚本
jest index.spec.js
复制代码
哎呀妈呀, 报错了, look look.

解决方案: 在项目目录下执行如下命令.
npm init
复制代码
安装完成后、项目根目录下就多了package.json文件了.

那我们再次执行E步骤:哎呀妈呀, 再次报错了, look look. 报错解析: 脚本里面至少要包含一个测试, 即类似it('xxx', () => expect('xxx').toBe('xxx'))的代码, 详细用法可以搜索下jest测试框架的教程.
解决方案: 加上至少一个测试用例(下面例子演示的是模拟文本的tap事件)。
最终的脚本代码如下:
const automator = require('miniprogram-automator')
describe('index', () => {
let miniProgram
let page
beforeAll(async () => {
miniProgram = await automator.launch({
projectPath: '/Users/susan.li/files/mini-demo',
cliPath: '/Applications/wechatwebdevtools.app/Contents/MacOS/cli'
})
page = await miniProgram.reLaunch('/pages/index/index')
await page.waitFor(5000)
}, 30000)
it('点击hello world文本', async () => {
await page.waitFor(3000)
// 通过.user-motto选择目标元素
const tabbar = await page.$('.user-motto')
tabbar.tap()
})
// afterAll(async () => {
// await miniProgram.close()
// })
})
复制代码
那我们再次执行E步骤: 我们会发现、在开发者工具下有执行tap方法.

脚本执行完毕后、终端会输出如下内容、告诉执行结果:
4.3 进阶玩法
上述做法需要在代码内注入微信开发者工具的安装路径&&项目路径, 不同项目成员内的配置是不同、那我们思考下能否做成隔离的?
即项目本身只维护测试用例脚本、至于在哪里执行脚本、执行的项目目录是什么、由开发者自行决定.
通过命令打开开发版微信开发者工具的自动化接口并连接自动化接口。 A. 找到微信开发者工具安装目录, 在该目录终端下输入如下命令:
// 进入微信开发者工具的安装目录(笔者的目录结构是: /Applications/wechatwebdevtools.app) -> /微信开发者工具安装目录/Contents/MacOS
cd /Applications/wechatwebdevtools.app/Contents/MacOS
// 找到要执行自动化测试的目录(笔者项目路径是:/Users/susan.li/files/mini-demo)
cli --auto /Users/susan.li/files/mini-demo --auto-port 9420
复制代码
Tips: 自动化端口是独立于服务端口的(你在开发者工具->设置->安全->打印出来的52968其实是服务器端口); 我们需要在终端看到如下提示才意味着成功打开了自动化端口(9420)
// 要看到这句话、这句话很关键!!!!!!
✔ Open project with automation enabled success /Users/susan.li/files/mini-demo
复制代码
我们看看实际效果图:

命令运行成功后、会自动打开开发者工具&项目、并在右上角有如下提示.
B. 找到自动化测试的项目根目录下, 执行如下命令安装SDK(若已安装、可忽略)
// 若需要安装、安装命令如下:
npm i miniprogram-automator --save-dev
复制代码
C. 引入自动化测试SDK, 在脚本中连接自动化操作端口
const automator = require('miniprogram-automator');
const miniProgram = automator.connect({
wsEndpoint: 'ws://localhost:9420',
})
复制代码
D. 根据业务需要编写对应的脚本并进行相关操作
const automator = require('miniprogram-automator')
describe('index', () => {
let miniProgram
// 运行测试前调用、启用自动化端口9420
beforeAll(async () => {
miniProgram = await automator.connect({
wsEndpoint: 'ws://localhost:9420',
})
})
// 运行测试后调用
afterAll(() => {
miniProgram.disconnect();
})
// 自动化测试内容
it('点击hello world文本', async () => {
// 获取页面相关信息
const page = await miniProgram.reLaunch('/pages/index/index')
// 通过.user-motto选择目标元素
const tabbar = await page.$('.user-motto')
// 模拟tap事件
tabbar.tap()
})
})
复制代码
E. 脚本准备完毕、执行如下命令做自动化测试(保持微笑开发者工具是打开的状态)
jest index.spec.js
复制代码
若测试通过、在终端会有如下PASS提示,结果如图所示:

5. 写在最后
作为电商类小程序, 保障线上业务的稳定运行、要求各页面各模块在异常情况下进行适当的兜底处理、确保给到用户最基础的用户体验。
此文仅作为自动化测试的初入门, 接下来会结合实际业务去做进一步梳理演示。
若有错误之处, 恳请留言, 定会及时更正!
若觉着对您有帮助的话恳请点个赞或着收藏吧!
最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走

我们学习软件测试必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。


这些资料,对于想进阶【自动化测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。希望对大家有所帮助…….

很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel
只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您