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

XCTest测试运行器启动后未执行测试求助(Vapor+Python依赖)

问题:Vapor XCTest环境中测试无法执行,进程超时终止

我正在用Vapor(搭配XCTVapor)和Python作为应用依赖搭建XCTest环境,但测试完全没执行。5分钟后进程终止,提示:
Test runner never began executing tests after launching

应用入口代码:

static func main() async throws {
    var env = try Environment.detect()
    try LoggingSystem.bootstrap(from: &env)
    
    guard let stdLibPath = Bundle.main.path(forResource: "python-stdlib", ofType: nil) else { return }
    guard let libDynloadPath = Bundle.main.path(forResource: "python-stdlib/lib-dynload", ofType: nil) else { return }
    setenv("PYTHONHOME", stdLibPath, 1)
    setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath)", 1)
    // Temporarily disable Python
    Py_Initialize()
    print ("Py_Initialize complete")
    
    // we now have a Python interpreter ready to be used
    let app = Application(env)
    defer { app.shutdown() }
    
    do {
        try await configure(app)
    } catch {
        app.logger.report(error: error)
        throw error
    }
    try await app.execute()
}

我能在app.execute()处触发断点,但后续无进展,测试用例断点从未触发,通过XCode构建。

调度线程spindump信息:

Thread 0x23a87 DispatchQueue "com.apple.main-thread"(1) 412 samples (1-412) priority 46 (base 46)
    412 start + 1903 (dyld + 25631) [0x7ff80675441f]
    412 main + 102 (entrypoint.swift in app + 14630) [0x1050d4926]
    412 ??? (libswift_Concurrency.dylib + 227060) [0x7ffc0beb16f4]
    412 ??? (libswift_Concurrency.dylib + 227107) [0x7ffc0beb1723]
    412 CFRunLoopRun + 40 (CoreFoundation + 1041893) [0x7ff806c0b5e5]
    412 CFRunLoopRunSpecific + 560 (CoreFoundation + 503489) [0x7ff806b87ec1]
    412 __CFRunLoopRun + 1365 (CoreFoundation + 506496) [0x7ff806b88a80]
    412 __CFRunLoopServiceMachPort + 145 (CoreFoundation + 511999) [0x7ff806b89fff]
    412 mach_msg + 19 (libsystem_kernel.dylib + 6282) [0x7ff806a6f88a]
    412 mach_msg_overwrite + 692 (libsystem_kernel.dylib + 34260) [0x7ff806a765d4]
    412 mach_msg2_trap + 10 (libsystem_kernel.dylib + 5538) [0x7ff806a6f5a2]
    *411 ipc_mqueue_receive_continue + 0 (kernel + 1528192) [0xffffff8000451180]
    *1 ipc_mqueue_receive_continue + 0 (kernel + 1528192) [0xffffff8000451180]

其他线程均来自NIO线程池(NIO-ELT-和TP-#),各16个线程。

测试文件代码:

@testable import app
//import XCTVapor
import PythonKit
//import XCTest
import XCTVapor

final class appTests: XCTestCase {
    override func setUp() async throws {
        try await super.setUp()
    }

    override func setUpWithError() throws {
        print ("setupWthError 1")
        try super.setUpWithError()
        print ("setupWthError 2")
    }

    func testPython() async throws {
        print("TestPython")
        let sys = Python.import("sys")
        XCTAssertNotNil(sys)
        print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)")
        print("Python Encoding: \(sys.getdefaultencoding().upper())")
        print("Python Path: \(sys.path)")

        _ = Python.import("math") // verifies `lib-dynload` is found and signed successfully
    }

    func testHelloWorld() async throws {
        let app = Application(.testing)
        defer { app.shutdown() }
        try await configure(app)

        try app.test(.GET, "hello", afterResponse: { res in
            XCTAssertEqual(res.status, .ok)
            XCTAssertEqual(res.body.string, "Hello, world!")
        })
    }
}

解决方案

1. 解除主线程阻塞(核心问题)

app.execute()会启动Vapor应用并持续监听请求,直接占用主线程陷入CFRunLoop循环,导致XCTest runner根本无法拿到主线程执行测试逻辑。修改入口代码,仅在非测试环境下启动服务:

static func main() async throws {
    var env = try Environment.detect()
    try LoggingSystem.bootstrap(from: &env)
    
    // 测试环境下直接返回,不启动服务
    guard env != .testing else { return }
    
    guard let stdLibPath = Bundle.main.path(forResource: "python-stdlib", ofType: nil) else { return }
    guard let libDynloadPath = Bundle.main.path(forResource: "python-stdlib/lib-dynload", ofType: nil) else { return }
    setenv("PYTHONHOME", stdLibPath, 1)
    setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath)", 1)
    Py_Initialize()
    print ("Py_Initialize complete")
    
    let app = Application(env)
    defer { app.shutdown() }
    
    do {
        try await configure(app)
    } catch {
        app.logger.report(error: error)
        throw error
    }
    try await app.execute()
}

2. 调整Python初始化逻辑

全局初始化Python可能干扰测试环境,建议将初始化逻辑移到单个测试用例内部,避免影响整个测试进程:

func testPython() async throws {
    print("TestPython")
    // 测试内部初始化Python,用完销毁
    guard let stdLibPath = Bundle.main.path(forResource: "python-stdlib", ofType: nil) else {
        XCTFail("找不到Python标准库")
        return
    }
    guard let libDynloadPath = Bundle.main.path(forResource: "python-stdlib/lib-dynload", ofType: nil) else {
        XCTFail("找不到Python动态库目录")
        return
    }
    setenv("PYTHONHOME", stdLibPath, 1)
    setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath)", 1)
    Py_Initialize()
    defer { Py_Finalize() }
    
    let sys = Python.import("sys")
    XCTAssertNotNil(sys)
    print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)")
    print("Python Encoding: \(sys.getdefaultencoding().upper())")
    print("Python Path: \(sys.path)")

    _ = Python.import("math")
}

3. 修复测试依赖导入

测试文件中注释掉了import XCTest,虽然XCTVapor可能间接导入,但明确导入能避免潜在问题:

import XCTest

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:21:01