Mousepose
屏幕上鼠标位置/按键高亮
关于 LeanCloud 的测试情况
- 存储框架,数据层.适合写单元测试
- 项目早起就有单元测试
- 提交一个 PR, Jenkins 上跑单元测试
- 覆盖率 80%
- 直接写测试, 用来检测正在开发的新接口
- 直接写测试, 重现 bug
- 积累的测试越多越强大
- 两种场景: 改完一些代码后, 本地跑相关的测试, 发现某些不通过: Jenkins 自动化跑测试的时候, 发现最新改动引起其他问题, 某些测试不通过
- 放心的发布与重构, 剩下手动验证的时间
为什么要写单元测试
- 当发现自己改了代码后,经常需要手动验证是否正常时
- 客户反馈问题, 使用测试找到问题
- 开发新接口, 编写相应代码后加入测试
- 发现改了代码后, 经常引起其他地方的 bug 时
- 多人合作的项目, 可能交给其他人的项目
- 高质量/流行的开源项目一般都有丰富的单元测试
怎么写单元测试
- 模块化代码, 数据层与 UI 层分离
- 最少的测试代码达到最高的覆盖率
- 异步处理
- 框架选择
- 覆盖率
expecta
框架平息
- expecta expect(error).not.beNil()
- specta describe("") it("")
- Kiwi describe("") it("")
- Tdd和 BDD 框架的坏处: 和 Xcode 结合不好, 比如没有测试按钮, 左边栏没有列全单元测试
iOS 单元测试异步处理
#define WAIT(name) \
do { \
[self expectationForNotification:name object:nil handler:nil]; \
[self waitForExpectationsWithTimeout:60 handler:nil]; \
} while(0)
#define NOTIFY(name) \
do { \
[[NSNotificationCenter defaultCenter] postNotificationName:name object:nil];\
} while(0)
- (void)waitNotification:(const void *)notification {
NSString *name = [NSString stringWithFormat:@"%p", notification];
[self expectationForNotification:name object:nil handler:nil];
[self waitForExpectationsWithTimeout:AV_YEAR_SECONDS handler:nil];
}
- (void)postNotification:(const void *)notification {
NSString *name = [NSString stringWithFormat:@"%p", notification];
[[NSNotificationCenter defaultCenter] postNotificationName:name object:nil];
}
覆盖率
从 Xcode7 开始,自带覆盖率高爆功能. 设置要点:
- Scheme 中开启 Gather Coverage Data
- 针对 App Target 来测试, 而非 Test Target
- We've Got You Covered
- 如何查看单元测试具体运行需要了解哪些代码
Xcode 7 之后的脚本
xcodebuild \
-workspace AVOS.scworkspace \
-scheme AVOSCloudIMTests \
-sdk iphonesimulator \
clean \
test && \
xcodebuild \
-workspace AVOS.scworkspace \
-scheme AVOSCloudTests \
-sdk iphonesimulator \
clean \
test \
远程自动化测试
- 搭建 Jenkins
- 本地空闲的 Mac 电脑或将 Mac 电脑拿到数据中心
- 测试脚本, 设置项目
- Github PR 构建插件: ghprb
- 配置 Jenkins Job
- 本地测试能发挥出测试的 70% 好处, 远程自动化则发挥出 100% 的好处
- Github 上配置 Setting->Webhooks ,添加搭建的自动化测试平台, 就可以进行自动化测试
Jenkins 配置
- 要让 Jenkins 能读到你的代码
- 配置 Hook, 当提交 Github PR 时, 发送响应的事件到 Jenkins 上
- Jenkins 要知道是哪一个 PR, 来拉取最新的代码
- 设置 Jenkins 测试脚本
- 当测试失败之后, 要让 Genkins 通知 Github 页面
- 测试失败后, 可发送通知到 Slack 或邮件
export LC_ALL="en_US.UTF-8"
pod install --verbose --no-repo-update
if [ -e test.sh] then
./test.sh
fi
Jenkins 如何知道测试失败或者成功
- 根据进程返回值. 如果是0, Jenkins 认为测试成功, 否则失败
- Xcode 7 之前, 尽管 Xcodebuild test 失败了, 依然返回0; Xcode 7 之后, 失败了返回65
Travis CI
travis-ci
- 设置 Test Scheme 为 shared
- Travis 上连接 Github 账号, 读取项目
- .travic.yml
- Travis 免费版可以给开源项目测试, 收费版给私有项目测试
- Travis 测试比较慢
- 如果会用 Jenkins 自己搭建自动化测试框架, Travis 眨眼间就会
language: objective-c
xcode_workspace: XXX.xcworkspace
xcode_scheme: XXX
远程打包发布
- 发布脚本
- Jenkins 上配置 Job 运行
- LeanCloud 上是发布框架, 远程打包 SDK 推送到 Github 仓库, 同步网站更新
- 针对 App, 四核没必要远程打包发布
实现思路
- Jenkins 读取你的代码
- 读取发布版本
- unlock keychain, 构建一个项目会弹出输入密码框, 来读取签名的 keychain
execute_command('security unlock-keychain -p "xxx" ~/Library/Keychains/login.keychain')
在命令行中解Keychain, 一般会弹框输入密码
Lioo
- 查看 framework 的编译架构 lipo -info
- lipo 合并不同架构的 framework
Pod
- 本地引用 Pod, --no-repo-update --verbose
- 发布 pod, podfile 配置
自动化 Pod 推送
def push_podspec_in_path(path)
iterate_r(path, 'podspec') do |file|
podspec_name = File.basename(file, ".podspec")
podspec_version = version[1..-1]
exists = is_podspec_exists(podspec_name, podsepc_version)
if exists
log("#{podspec_name} #{podsepc_version} exists!")
next
else
log("#{podspec_name} #{podsepc_version} not exists, now try to push it")
end
ok = false
for i in 0..10
ok = system("pod truck push #{file} --verbose --allow-warning")
if ok
log("success to push #{file}")
break
else
log("failed to push #{file}")
end
end
if !ok
exit_with_info("")
end
end
end
重复推送 Podspec
def is_podspec_exists(name, version)
podspec_url = "https://github.com/CocoaPods/Specs/blob/master/Specs/#{name}/#{version}/#{name}"
url = URI.parse(podspec_url)
req = Net::HTTP::Get.new(url.to_s)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
res = http.request(req)
res.code == '200'
end
好用的工具
- Revel(越狱) 分析任意 app 的 ui 界面
- Flex(越狱) 分析任意 App 的网络请求, ui 界面, 本地文件, NSUserDefaults, Log 等