# UI 自动化测试

> TestCase/TestSuite 在图形运行时中运行，并对截图进行视觉比对（golden images）。

Source: https://crossfly.dev/zh/docs/best-practices/testes — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# UI 自动化测试

CrossFly 的测试框架**不是 JUnit**。每个 `TestCase` 都**在图形运行时内部**运行（绘制线程、`EventsProcessor`、GL 管线），而主要断言是把截图与参考图像（*golden images*）进行**视觉比对**。通往第 31 章的入口。

## `TestCase` 剖析
```java
public class LoginTest extends TestCase {
   @Override protected void load() throws Exception {
      new LoginScreen().show();        // 构建被测界面
   }
   @Override public void runTests() throws Exception {
      waitWindow(LoginScreen.class);
      type(0, TextField.class, "usuario@app.com");   // 在第 1 个 TextField 中输入
      type(1, TextField.class, "senha123");
      click("Entrar");                 // 点击带此文本的按钮
      shot();                          // 截图并与 golden 对比
      waitWindow(HomeScreen.class);
      shot();
   }
   @Override protected void unload() { /* 清理状态 */ }
}
```
交互：`click(...)`、`type(...)`、`enter()`、`penDown/penUp/penDrag(...)`、`setText(...)`。
查找：`find(index, Classe)` / `find(texto, Classe)`。同步：`waitWindow(...)`、`waitAnimation()`、`waitUIidle(...)`。

## 分组与运行——`TestSuite`
```java
TestSuite suite = new TestSuite();
suite.startTests(new TestCase[]{ new LoginTest(), new CadastroTest() });
```
测试套件会运行每个测试，将各 `shot()` 与 golden 图像比对，并给出报告（`success`/`fail`/`log`）。

## ⚠️ 确定性是前提：`DeviceConfig.isTesting`
只有当渲染是**确定性的**时，逐像素比对才能匹配。开启 `DeviceConfig.isTesting = true`——这会固定光标、移除时间戳，并让 `TimePicker`/时钟保持稳定。**没有它，golden 永远不会匹配**（闪烁的光标、当前时间等都会改变图像）。

## 无 UI：`WindowlessTestCase`
对于不需要屏幕的逻辑（仓储、格式化），继承 `WindowlessTestCase`——它在运行时中运行，但不构建 UI。

## 生成文档图像
同样的测试截图可用于**生成文档/商店的图像**：`Main.getScreenShot(true).toPng("prefixo_")`。

## 最佳实践
- 在测试批次中**始终**设置 `DeviceConfig.isTesting = true`。
- 在 `shot()` 之前使用 `waitWindow(...)`/`waitAnimation()`——捕获一个**稳定**的帧。
- 每个相关的视觉状态对应一个 golden；有意改动 UI 时重新生成它们。

## 常见错误
- Golden "有时通过、有时不通过" → 缺少 `isTesting`/`waitAnimation`（帧不稳定）。
- 在窗口尚未完全可见时就 `shot()`。
