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

单元测试:如何覆盖类中调用Singleton的代码行?

单元测试覆盖Singleton调用的解决方案

问题场景

我需要测试ClassToTest类中对单例MySingleton的调用,当前Xcode测试能通过,但SonarQube提示未覆盖到MySingleton.shared.sendAnalytics("params")这行代码,该如何处理?

待测试类代码

class ClassToTest (){
    var testMessage = ""

    init(){
        // 空初始化方法
    }

    func funcWhichHasASingleton() {
        testMessage = "Function was covered"

        MySingleton.shared.sendAnalytics("params")
    }
}

现有测试代码

func testExample() {
    // Given
    let sut = ClassToTest()

    // When
    sut.funcWhichHasASingleton()

    // Then
    XCTAssertEqual(sut.testMessage, "Function was covered")
}

解决方案

要覆盖并验证单例调用,核心是解耦单例依赖,让测试能控制或验证单例的行为,以下是几种可行方案:

方案1:通过依赖注入解耦(推荐)

修改ClassToTest,将单例依赖通过初始化方法或属性注入,而非直接硬编码调用MySingleton.shared:

首先定义抽象协议,解耦具体实现:

// 抽象统计服务的行为
protocol AnalyticsService {
    func sendAnalytics(_ params: String)
}

// 让原有单例遵循协议
class MySingleton: AnalyticsService {
    static let shared = MySingleton()
    private init() {}
    
    func sendAnalytics(_ params: String) {
        // 原有业务逻辑实现
    }
}

修改ClassToTest,依赖协议而非具体单例,同时保留默认使用单例的逻辑,不影响原有业务代码:

class ClassToTest {
    var testMessage = ""
    private let analyticsService: AnalyticsService

    // 默认注入单例,业务代码无需修改
    init(analyticsService: AnalyticsService = MySingleton.shared) {
        self.analyticsService = analyticsService
    }

    func funcWhichHasASingleton() {
        testMessage = "Function was covered"
        // 调用协议方法,而非直接调用单例
        analyticsService.sendAnalytics("params")
    }
}

测试时使用Mock类记录调用行为:

// Mock类,用于验证调用情况
class MockAnalyticsService: AnalyticsService {
    var sendAnalyticsCalled = false
    var receivedParams: String?

    func sendAnalytics(_ params: String) {
        sendAnalyticsCalled = true
        receivedParams = params
    }
}

func testFuncWhichHasASingleton() {
    // Given
    let mockAnalytics = MockAnalyticsService()
    let sut = ClassToTest(analyticsService: mockAnalytics)

    // When
    sut.funcWhichHasASingleton()

    // Then
    XCTAssertEqual(sut.testMessage, "Function was covered")
    // 验证统计方法是否被调用,参数是否正确
    XCTAssertTrue(mockAnalytics.sendAnalyticsCalled)
    XCTAssertEqual(mockAnalytics.receivedParams, "params")
}

这种方式既覆盖了原有的单例调用代码,又能验证其行为逻辑,同时解耦了依赖,提升代码可维护性。

方案2:给单例添加测试钩子(快速适配)

如果不想修改ClassToTest的结构,可以给MySingleton添加测试专用的实例替换入口,仅在测试环境生效:

class MySingleton {
    static var shared: MySingleton = MySingleton()
    private init() {}
    
    func sendAnalytics(_ params: String) {
        // 原有业务逻辑实现
    }

    // 测试专用:替换单例实例
    static func setSharedInstance(_ instance: MySingleton) {
        shared = instance
    }
}

测试时创建Mock子类替换原单例:

class MockMySingleton: MySingleton {
    var sendAnalyticsCalled = false
    var receivedParams: String?

    override func sendAnalytics(_ params: String) {
        sendAnalyticsCalled = true
        receivedParams = params
    }
}

func testFuncWhichHasASingleton() {
    // Given
    let mockSingleton = MockMySingleton()
    MySingleton.setSharedInstance(mockSingleton)
    let sut = ClassToTest()

    // When
    sut.funcWhichHasASingleton()

    // Then
    XCTAssertEqual(sut.testMessage, "Function was covered")
    XCTAssertTrue(mockSingleton.sendAnalyticsCalled)
    XCTAssertEqual(mockSingleton.receivedParams, "params")

    // 测试结束后恢复原单例,避免影响其他测试用例
    MySingleton.setSharedInstance(MySingleton())
}

这种方法无需修改待测试类,仅需给单例增加少量测试代码,适合快速适配现有项目。

方案3:使用第三方测试框架(如OCMock)

如果项目允许引入第三方测试框架,比如OCMock,可以直接Mock单例实例来验证调用:

import OCMock

func testFuncWhichHasASingleton() {
    // Given
    let mockSingleton = OCMClassMock(MySingleton.self)
    // Stub单例的shared方法,返回Mock实例
    OCMStub(mockSingleton.shared).andReturn(mockSingleton)
    let sut = ClassToTest()

    // When
    sut.funcWhichHasASingleton()

    // Then
    XCTAssertEqual(sut.testMessage, "Function was covered")
    // 验证sendAnalytics方法是否被调用,参数是否匹配
    OCMVerify([mockSingleton sendAnalytics:@"params"])

    // 清理Mock实例
    mockSingleton.stopMocking()
}

这种方式无需修改业务代码,但需要引入第三方依赖,适合已使用相关测试框架的项目。

内容的提问来源于stack exchange,提问作者Javier Landa-Torres

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:05:45