You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在XCTest框架中根据测试用例结果触发任务并传失败标识?

解决Swift XCTest测试失败时触发自定义方法并传入失败标识的问题

我来帮你搞定这个需求~要实现测试用例失败时触发自定义任务(比如截图)并传入指定的失败ID,这里有两种可靠的实现方式,一步步给你说明:

方法一:重写XCTestCase的recordFailure方法(适配现有断言代码)

XCTest框架在断言失败时会自动调用recordFailure(withDescription:inFile:atLine:expected:)方法,我们可以重写这个方法来捕获失败事件,并从你现有断言的描述文本里提取标记的失败ID(比如你用的---10385格式)。

修改你的测试类代码如下:

import Foundation
import XCTest

class HomePageChromeUITests: XCTestCase {
    var pathStr:String = ""
    
    override func setUp() {
        super.setUp()
        continueAfterFailure = false
        self.launchWithUserLoggedIn()
    }
    
    override func tearDown() {
        super.tearDown()
    }
    
    // 重写失败记录方法,捕获断言失败事件
    override func recordFailure(withDescription description: String, inFile filePath: String, atLine lineNumber: Int, expected: Bool) {
        // 先调用父类方法,保证XCTest默认的失败记录行为正常执行
        super.recordFailure(withDescription: description, inFile: filePath, atLine: lineNumber, expected: expected)
        
        // 从断言描述中提取失败ID(匹配"---xxxx"格式)
        if let failureID = extractFailureID(from: description) {
            // 调用自定义的失败处理方法
            handleTestFailure(withFailureID: failureID)
        }
    }
    
    // 自定义失败处理方法,这里可以添加截图等业务逻辑
    private func handleTestFailure(withFailureID failureID: String) {
        print("测试失败,失败ID:\(failureID)")
        // 示例:添加截图逻辑
        let screenshot = XCUIScreen.main.screenshot()
        // 可将截图保存到指定路径,文件名带上失败ID便于区分
        let savePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            .appendingPathComponent("failure_screenshot_\(failureID).png")
        try? screenshot.pngRepresentation.write(to: savePath)
    }
    
    // 解析断言描述中的失败ID
    private func extractFailureID(from description: String) -> String? {
        // 匹配"---"后面的数字序列
        let pattern = #"---(\d+)"#
        guard let regex = try? NSRegularExpression(pattern: pattern),
              let match = regex.firstMatch(in: description, range: NSRange(description.startIndex..., in: description)) else {
            return nil
        }
        let range = Range(match.range(at: 1), in: description)
        return range.map { String(description[$0]) }
    }
    
    func testHomePageChrome_10385() {
        let app = XCUIApplication()
        let backButton = app.buttons[AccessibilityIdentifiers.PageEditorBackButton]
        let editButton = app.buttons[AccessibilityIdentifiers.PageEditorEditButton]
        XCTAssertTrue(backButton.exists && backButton.frame.origin.x < editButton.frame.origin.x, "Back button does not exist.---10385")
        XCTAssertTrue(!editButton.exists, "Edit button does not exist.---10385")
    }
}

这个方案不需要修改你现有的断言代码,只要保持断言描述里的---失败ID格式,就能自动提取ID并触发处理逻辑。

方法二:自定义断言函数(更可控、更可靠)

如果不想依赖字符串解析,我们可以自己封装断言函数,直接通过参数传递失败ID,这样逻辑更清晰,也避免了因为描述格式变化导致的解析错误:

import Foundation
import XCTest

class HomePageChromeUITests: XCTestCase {
    var pathStr:String = ""
    
    override func setUp() {
        super.setUp()
        continueAfterFailure = false
        self.launchWithUserLoggedIn()
    }
    
    override func tearDown() {
        super.tearDown()
    }
    
    // 自定义断言函数:验证条件为true,失败时触发处理方法
    private func XCTAssertTrueWithFailureID(_ expression: @autoclosure () throws -> Bool, failureID: String, _ message: String = "", file: StaticString = #filePath, line: UInt = #line) {
        do {
            guard try expression() else {
                // 先触发失败处理逻辑
                handleTestFailure(withFailureID: failureID)
                // 调用原生XCTFail抛出失败
                XCTFail(message, file: file, line: line)
                return
            }
        } catch {
            handleTestFailure(withFailureID: failureID)
            XCTFail("断言执行出错:\(error)", file: file, line: line)
        }
    }
    
    // 自定义失败处理方法
    private func handleTestFailure(withFailureID failureID: String) {
        print("测试失败,失败ID:\(failureID)")
        // 示例:添加截图并保存
        let screenshot = XCUIScreen.main.screenshot()
        let desktopPath = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first!
        let screenshotPath = desktopPath.appendingPathComponent("failure_\(failureID).png")
        try? screenshot.pngRepresentation.write(to: screenshotPath)
    }
    
    func testHomePageChrome_10385() {
        let app = XCUIApplication()
        let backButton = app.buttons[AccessibilityIdentifiers.PageEditorBackButton]
        let editButton = app.buttons[AccessibilityIdentifiers.PageEditorEditButton]
        
        // 使用自定义断言,直接传入失败ID
        XCTAssertTrueWithFailureID(backButton.exists && backButton.frame.origin.x < editButton.frame.origin.x, 
                                   failureID: "10385", 
                                   "Back button does not exist.")
        XCTAssertTrueWithFailureID(!editButton.exists, 
                                   failureID: "10385", 
                                   "Edit button does not exist.")
    }
}

这个方案的优势是逻辑更直接,不需要依赖字符串解析,失败ID的传递更可靠,也方便后续扩展其他断言类型(比如XCTAssertEqualWithFailureID)。

额外提示

如果需要在测试通过或被跳过时也触发任务,可以重写testRun方法,监听测试的状态变化:

override var testRun: XCTestRun? {
    didSet {
        // 监听测试状态,比如测试完成后判断状态
        testRun?.completionBlock = { [weak self] in
            guard let self = self else { return }
            switch self.testRun?.testStatus {
            case .passed:
                self.handleTestPassed()
            case .skipped:
                self.handleTestSkipped()
            default:
                break
            }
        }
    }
}

private func handleTestPassed() {
    print("测试通过")
    // 添加通过时的任务逻辑
}

private func handleTestSkipped() {
    print("测试被跳过")
    // 添加跳过的任务逻辑
}

内容的提问来源于stack exchange,提问作者Automation User

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:57:22