【问题标题】:XCTestCase to check if a method is called within a StructXCTestCase 检查是否在 Struct 中调用了方法
【发布时间】:2016-09-16 05:54:32
【问题描述】:

我正在尝试测试一段代码,我在其中检查帐户是否已创建密钥并将其存储在钥匙串中。如果不是,它会调用一个启动 oauth 进程的方法。

如果用户没有密钥,我的第一个想法是覆盖我想调用的方法。但是我使用的是结构,因此无法继承和覆盖该方法。

如果我使用的是一个类,我会这样:

func testInitiateOauthCalledIfSecretKeyNotFound() {

    class MockKeychainAccess: KeychainAccess {
       var initiateAuthorizationWasCalled: Bool = false
        override initiateAuthorization() {
             initiateAuthorizationWasCalled = true
        }

    let keychainAccess = MockKeychainAccess()
    keychainAccess.authorizeWithGoogle()
    XCTAssertTrue(initiateAuthorizationWasCalled)
}

我没有测试过这段代码,所以不确定它是否可以编译。但是从逻辑上讲,它似乎可以处理我所追求的情况。如果在authorizeWithGoogle 方法中我们调用initiateAuthorization(),那么我就会知道这已经发生了。但是,当他们使用结构时,我们不能这样做,因为我们不能从结构继承。

请注意:我是 TDD 新手,所以我可能想错了。欢迎提出其他建议。但是,我不想仅仅为了编写测试而从结构转换为类。我正在使用结构,因为我想更快速。

有谁知道我可以测试一个函数是否在结构中被调用的方法?

===========

编辑:

为了回应 dasdom 的回答,我添加了一个我试图实现的一般方法的示例:

override func viewDidLoad() {
    setupView()

    let api = DataApi()
    getData(api)
}

func setupView() {
    tableView.dataSource = tableViewDataSource
}

func getData(api: DataApi) {

    api.getApplicationData( { (objects) in
        if let applications = objects as? [Application] {
            self.tableViewDataSource.setApplicationItems(applications)
            self.tableView.reloadData()
        }
        else {
            // Display error
        }
    })

}

所以我想注入 MockDataApi 以便它可以返回我想要的,因为该方法采用了一种 DataApi。但是我不确定我应该如何创建这个 MockDataApi 结构并将其传递给这个方法。

有人可以帮助了解如何为此目的构建这个模拟对象并使用它吗?我意识到它与协议有关,但很难将其拼凑在一起。

【问题讨论】:

  • 我不明白你的意思。你能详细说明你的问题吗?我添加了一个代码示例,如果他们使用一个类来演示我想要实现的目标,他们可以使用该示例。但是我使用的是结构,因此无法继承和覆盖该方法。我不确定如何进行与我使用结构而不是类编写的测试类似的测试。
  • 这听起来不错,但我不明白是什么让 KeychainAccess 成为结构。是在你自己的项目中吗?如果这是限制因素,那么将其提升为类可能是您需要的,因此您可以覆盖 func,如图所示。请注意,我自己的项目中没有类似的示例。
  • @bneely。谢谢你的评论。是的,它在我自己的项目中。但是我意识到我把它命名得很糟糕,并且将进行重构。它更像是一个 AuthorizationAccess 类/结构,它包含了用于执行 Oauth 和钥匙串保存的方法。是的,我开始认为进行这些测试的唯一方法是使用一个类。

标签: ios swift struct xctest xctestcase


【解决方案1】:

使用协议。使您的类/结构和您的测试模拟符合协议。注入依赖项并断言在你的模拟中调用了预期的方法。

编辑:示例

protocol DataApiProtocol {
    func getApplicationData(block: [AnyObject] -> Void)
}

// Production code
struct DataApi: DataApiProtocol {
    func getApplicationData(block: [AnyObject] -> Void) {
        // do stuff
    }

    // more properties and methods
}

// Mock code
struct MockDataApi: DataApiProtocol {
    var getApplicationDataGotCalled = false
    func getApplicationData(block: [AnyObject] -> Void) {
        getApplicationDataGotCalled = true
    }
}

// Test code
func testGetData_CallsGetApplicationData() {
    let sut = MyAwesomeClass()
    let mockDataApi = MockDataApi()        

    sut.getData(mockDataApi)

    XCTAssertTrue(mockDataApi.getApplicationDataGotCalled)
}

我希望这会有所帮助。

【讨论】:

  • 嗨 dasdom,感谢您的回复。抱歉回复晚了。我已经编辑了我的答案以显示一个示例场景。我想使用协议,但我不完全了解如何创建模拟对象并让它调用协议方法。
  • 我添加了一个例子。
  • 确实很有帮助。另一个问题。似乎通过让 MockDataApi 有一个方法来改变你需要协议发生变异的结构,这意味着真正的结构需要处理变异。即 - 它必须是一个 var 而不是让。我不喜欢变异函数,我更喜欢返回一个新结构以避免变异状态,因此更愿意让我的协议不受 mutating 关键字的影响。但是,出于测试目的,您对模拟对象作为一个类而不是符合协议的类有何看法?所以:类 MockDataApi: DataApiProtocol { }
猜你喜欢
  • 2014-02-02
  • 2019-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-12
  • 1970-01-01
相关资源
最近更新 更多