EarlGrey - iOS UI自动化测试框架

项目标题与描述

EarlGrey是由Google开发的iOS原生UI自动化测试框架,支持Objective-C和Swift语言。它是一个白盒测试解决方案,深度集成XCTest框架,可直接在Xcode的Test Navigator中运行测试。

核心价值:

  • 自动同步UI状态、网络请求和各种队列
  • 提供丰富的交互API和断言功能
  • 支持编写清晰简洁的测试代码
  • 与Xcode完美集成,可直接从IDE运行测试

注意:EarlGrey 1.0已停止维护,推荐使用集成XCUITest的EarlGrey 2.0

功能特性

核心功能

  • 智能同步机制:自动等待UI进入稳定状态
  • 丰富的交互API:点击、滑动、长按等手势操作
  • 元素定位器:多种方式定位UI元素(Accessibility ID、文本等)
  • 断言验证:丰富的断言方法验证UI状态
  • WebView支持:可测试嵌入的Web内容
  • 多手势支持:支持多指滑动手势

独特价值

  • 稳定性保障:内置同步机制减少测试 flakes
  • 精确控制:可自定义手势参数(持续时间、方向等)
  • 调试友好:失败时自动记录视图层次结构
  • 扩展性强:支持自定义匹配器和动作

安装指南

CocoaPods安装

  1. 安装EarlGrey gem:
    gem install earlgrey
    
  2. 在项目目录下运行:
    pod install
    
  3. 打开生成的xcworkspace文件

Carthage安装

  1. 安装依赖工具:
    brew install carthage
    xcode-select --install
    gem install earlgrey
    
  2. 初始化EarlGrey:
    earlgrey install --carthage --swift-version=3.0 --target=EarlGreyExampleSwiftTests
    carthage update EarlGrey --platform ios
    

系统要求

  • Xcode 10.0+
  • iOS 9.0+
  • CocoaPods 1.0.0+

使用说明

基础测试示例

// 选择并点击按钮
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"ClickMe")]
    performAction:grey_tap()];

// 验证标签文本
[[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(@"textLabel")]
    assertWithMatcher:grey_text(@"Expected Text")];

滑动操作

// 向下滑动50点
[[EarlGrey selectElementWithMatcher:grey_kindOfClass([UIScrollView class])]
    performAction:grey_scrollInDirection(kGREYDirectionDown, 50)];

高级功能

// 自定义长按手势(持续2秒)
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"LongPressArea")]
    performAction:grey_longPressWithDuration(2.0)];

// 多指滑动手势(3指同时滑动)
GREYMultiFingerSwipeAction *swipe = 
    [[GREYMultiFingerSwipeAction alloc] initWithDirection:kGREYDirectionUp
                                                duration:1.0
                                         numberOfFingers:3];
[[EarlGrey selectElementWithMatcher:grey_anyOf(grey_kindOfClass([UIView class]), nil)]
    performAction:swipe];

核心代码

基础交互实现

// GREYTapAction.m 点击动作实现
- (BOOL)perform:(id)element error:(__strong NSError **)errorOrNil {
    if (![self satisfiesConstraintsForElement:element error:errorOrNil]) {
        return NO;
    }
    
    UIView *viewToTap = [element isKindOfClass:[UIView class]] ? 
        element : [element grey_viewContainingSelf];
    UIWindow *window = [viewToTap isKindOfClass:[UIWindow class]] ? 
        (UIWindow *)viewToTap : viewToTap.window;
    
    return [GREYTapper tapOnWindow:window
                     numberOfTaps:_numberOfTaps
                        location:[self grey_tapPointForElement:element]
                           error:errorOrNil];
}

同步机制实现

// GREYUIThreadExecutor.m 主线程同步
- (void)executeSync:(GREYExecutionBlock)block {
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_semaphore_t waitForBlock = dispatch_semaphore_create(0);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            block();
            dispatch_semaphore_signal(waitForBlock);
        });
        
        dispatch_semaphore_wait(waitForBlock, DISPATCH_TIME_FOREVER);
    }
}

元素定位实现

// GREYElementMatcher.m 元素匹配逻辑
- (BOOL)matches:(id)item {
    if (![self checkClassAndVisibility:item]) {
        return NO;
    }
    
    for (id<GREYMatcher> matcher in _matchers) {
        if (![matcher matches:item]) {
            return NO;
        }
    }
    
    return YES;
}

更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
公众号二维码

posted @ 2025-07-01 09:01  qife  阅读(37)  评论(0)    收藏  举报