feature: Weex APM

This commit is contained in:
FantasticLBP
2025-12-30 21:07:15 +08:00
parent 1142064d28
commit 7ac7513900
158 changed files with 9880 additions and 280 deletions

View File

@@ -1177,6 +1177,45 @@ SPEC_END
## 五、UI 测试
### 概念
UI 测试:属于端到端测试,是从应用程序启动到结束的测试过程。完全按照用户与应用程序交互的方式来复制与应用程序的交互。比但愿测试慢得多,运行起来更消耗资源。
### 测试原则
FIRST
- Fast测试模块应该是快速高效的
- Independent/Isolated测试模块应该是独立、互相不影响的
- Repeatable测试实例应该是可以重复使用的测试结果应该是相同的
- Self-validating测试应完全自动化。输出结果要么成功、要么失败
- Timely理想情况下应该在编写要测试的生产代码之前编写测试测试驱动开发
### 需要测试什么?
- 视觉表现验证
- 元素渲染控件尺寸、颜色、字体、图标资源accessibilityIdentifier 定位)
- 布局规则动态布局Auto Layout 约束断裂检测)、横竖屏适配、多语言截断
- 动效完整性:转场动画时长、交互反馈(如按钮点击态)
- 交互行为验证
| 交互类型 | 测试要点 | 工具示例 |
| :----------- | :----------------------------------------------- | :------------------------------------- |
| **手势操作** | 滑动/长按/捏合等触发事件 | `XCUITest: swipeUp()` |
| **表单输入** | 键盘类型切换、输入校验(正则)、自动填充 | `typeText("test@email.com")` |
| **导航流** | 页面跳转栈深度、返回逻辑(物理返回 vs 程序返回) | `navigationBars.buttons["Back"].tap()` |
| **异步状态** | 加载中/空状态/错误页的显示与隐藏 | `waitForExistence(timeout: 5)` |
- 数据驱动验证
- API 数据映射Mock 不同 API 响应200/404/500检查 UI 渲染正确性
- 本地数据同步Core Data/Realm 更新后 UI 即时刷新
- 动态内容:富文本(含超链接)、图片懒加载、视频播放器状态
- 边界场景验证Edge Cases
- 设备兼容:从 iPhone SE 到 iPad Pro 的适配
- 系统版本iOS 14~17 的关键行为差异(如权限弹窗样式)
- 极端操作:快速连续点击、低内存告警恢复
- 无障碍支持VoiceOver 焦点顺序、Dynamic Type 超大字体布局
### 基础使用
上面文章大篇幅的讲了单元测试相关的话题,单元测试十分适合代码质量、逻辑、网络等内容的测试,但是针对最终产物 App 来说单元测试就不太适合了,如果测试 UI 界面的正确性、功能是否正确显然就不太适合了。Apple 在 Xcode 7 开始推出的 `UI Testing` 就是苹果自己的 UI 测试框架。
@@ -1339,16 +1378,194 @@ int main(int argc, char * argv[]) {
### 探
### 单元测试的原理窥
开发的单元测试代码,运行的背后也是一个可执行文件。查看内部,可以发现一堆和测试相关的 framework。
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/XcodeUnitTestDyanimcFramework.png" style="zoom:30%" />
思考:一个工程中,只要写好了测试代码,是不是加载这几个动态库就可以运行测试用例了?
思考:一个非 UI 测试工程(正常的 App 工程),是不是加载这几个测试相关的动态库,写好测试代码,就可以运行测试用例了?
细节不贴了。Demo App 引入 `XCTest.framework` 后业务代码里即可引入 `#import <XCTest/XCTest.h>` 然后就可以编写测试代码了。
写法1
```objective-c
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 测试管理集
XCTestSuite *suite = [XCTestSuite defaultTestSuite];
// 初始化 TestCase
LoginUITests *loginTest = [LoginUITests testCaseWithSelector:@selector(testDidClickLoginAction)];
// 添加测试用例到当前 suite
[suite addTest:loginTest];
// 遍历并运行测试用例
for (XCTest *test in suite.tests) {
[test runTest];
}
}
```
写法2:
```objective-c
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 测试管理集
XCTestSuite *suite = [XCTestSuite testSuiteForTestCaseClass:LoginUITests.class];
// 初始化 TestCase
LoginUITests *loginTest = [LoginUITests new];
// 添加测试用例到当前 suite
[suite addTest:loginTest];
// 遍历并运行测试用例
for (XCTest *test in suite.tests) {
[test runTest];
}
}
```
#### 执行时机
这种情况下,整体流程为:
```mermaid
graph TD
A[测试套件启动] --> B[创建测试类实例]
B --> C1[执行 testMethod1]
C1 --> D1[调用 setUp]
D1 --> E1[执行 testMethod1 本体]
E1 --> F1[调用 tearDown]
B --> C2[执行 testMethod2]
C2 --> D2[调用 setUp]
D2 --> E2[执行 testMethod2 本体]
E2 --> F2[调用 tearDown]
B --> C3[执行 testMethod3]
C3 --> D3[调用 setUp]
D3 --> E3[执行 testMethod3 本体]
E3 --> F3[调用 tearDown]
```
分析:
- 当有多个测试方法时XCTest 会为**每个测试方法创建单独的类实例**
- 每个测试方法执行时都会触发完整的生命周期
```objective-c
// 伪代码展示 XCTest 内部执行流程
for (XCTest *test in allTests) {
[test invokeTest]; // 实际执行入口
}
// invokeTest 内部实现:
- (void)invokeTest {
[self setUp]; // 每次测试前调用
[self performTest]; // 执行测试方法本体
[self tearDown]; // 每次测试后调用
}
```
- 三个测试方法的执行示例
测试代码:
```objective-c
- (void)testValidLogin { ... }
- (void)testInvalidPassword { ... }
- (void)testNetworkErrorHandling { ... }
```
实际执行顺序
```objective-c
// 测试1
[LoginUITests setUp];
[LoginUITests testValidLogin];
[LoginUITests tearDown];
// 测试2
[LoginUITests setUp]; // 全新状态!
[LoginUITests testInvalidPassword];
[LoginUITests tearDown];
// 测试3
[LoginUITests setUp]; // 再次重置状态
[LoginUITests testNetworkErrorHandling];
[LoginUITests tearDown];
```
思考:为什么需要每次重置?
1. **测试隔离原则**
- 防止测试间的状态污染
- 确保每个测试都是独立可重复的
2. **资源管理**
- 每次 `tearDown` 释放测试占用的资源
- 避免内存泄漏累积
3. **环境一致性**
- `setUp` 确保每次测试初始条件相同
- 不受前次测试副作用影响
QA单独执行每个测试方法都会走 setup 和 teardown。按了快捷键 command + u运行所有的测试 case会执行几次 setup比如 Login 有3个测试 case。点击后执行流程是什么样的
```mermaid
sequenceDiagram
participant X as XCTestRunner
participant C as LoginUITests Class
participant I1 as 实例1 (testA)
participant I2 as 实例2 (testB)
participant I3 as 实例3 (testC)
X->>C: 调用 +[LoginUITests setUp] (类方法)
activate C
X->>I1: 创建实例1 (testA)
activate I1
I1->>I1: -setUp (实例方法)
I1->>I1: -testA (测试方法)
I1->>I1: -tearDown (实例方法)
deactivate I1
X->>I2: 创建实例2 (testB)
activate I2
I2->>I2: -setUp (实例方法)
I2->>I2: -testB (测试方法)
I2->>I2: -tearDown (实例方法)
deactivate I2
X->>I3: 创建实例3 (testC)
activate I3
I3->>I3: -setUp (实例方法)
I3->>I3: -testC (测试方法)
I3->>I3: -tearDown (实例方法)
deactivate I3
X->>C: 调用 +[LoginUITests tearDown] (类方法)
deactivate C
```
分析:
- 类级别初始化(只执行一次)。`+[LoginUITests setUp]` 类方法
- 在**所有测试开始前**执行一次
- 适合做全局初始化(如启动模拟服务器)
- 执行频率1次/测试类
- 每个测试方法的独立执行。对于每个测试方法testA, testB, testC
- 创建**新的测试类实例**
- **-setUp** 实例方法(每个测试方法前执行)
- 执行测试方法(如 **-testA**
- **-tearDown** 实例方法(每个测试方法后执行)
- 类级别清理(只执行一次)。`+[LoginUITests tearDown]` 类方法
- 在**所有测试结束后**执行一次
- 适合做全局清理(如关闭模拟服务器)
- 执行频率1次/测试类
## 六、精准测试
@@ -1369,7 +1586,7 @@ int main(int argc, char * argv[]) {
下面是之前在有赞开发完精准测试系统后落地到一个业务项目中取得的价值帮助2位 QA 发现漏测的代码,倒逼 QA 去设计更完善的测试 case、分析覆盖率低是开发者的兜底代码太多还是真的漏掉了业务 case。
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/iOS_PreciousUnitTest1.png" style="zoom:20%;display:inline"> <img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/iOS_PreciousUnitTest2.png" style="zoom:20%;display:inline">
<img src="./../assets/iOS_PreciousUnitTest1.png" style="zoom:20%;display:inline"> <img src="./../assets/iOS_PreciousUnitTest2.png" style="zoom:20%;display:inline">
精准测试助力业务,质量更加稳定。
@@ -1394,3 +1611,5 @@ UITesting 还是建议在核心逻辑且长时间没有改动的情况下去做
![测试占比](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/2020-07-14-TestingPercentage.png)
WWDC 这张图也很清楚UI 其实需要的占比较小,还是要靠单测驱动。
##