关于自动化测试的学习笔记

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 开始,自带覆盖率高爆功能. 设置要点:

  1. Scheme 中开启 Gather Coverage Data
  2. 针对 App Target 来测试, 而非 Test Target
  3. We've Got You Covered
  4. 如何查看单元测试具体运行需要了解哪些代码
关于自动化测试的学习笔记_第1张图片
覆盖率.png

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 等

你可能感兴趣的:(关于自动化测试的学习笔记)