swift - 如何在结构/类中 stub Swift "Trait/Mixin"方法进行测试

标签 swift unit-testing traits mixins stub

我最近了解到如何通过创建协议(protocol)并使用默认实现扩展该协议(protocol)来将“Traits/Mixins”添加到 Swift 中的结构/类。这很棒,因为它允许我添加功能来查看 Controller ,而不必向所述 View Controller 添加一堆帮助对象。我的问题是,如何 stub 这些默认实现提供的调用?

这是一个简单的例子:

protocol CodeCop {
  func shouldAllowExecution() -> Bool
}

extension CodeCop {
  func shouldAllowExecution() -> Bool {
    return arc4random_uniform(2) == 0
  }
}

struct Worker : CodeCop {
  func doSomeStuff() -> String {
    if shouldAllowExecution() {
       return "Cop allowed it"
     } else {
       return "Cop said no"
    }
  }
}

如果我想写两个测试,一个验证当 CodeCop 不允许执行时 doStuff() 返回字符串“Cop allowed it”,另一个测试验证字符串“Cop said no”返回通过 doStuff() 当 CodeCop 不允许执行时。

最佳答案

这很简单,只需在您的测试目标中编写一个附加协议(protocol)即可,该协议(protocol)称为 CodeCopStub,它继承自 CodeCop:

protocol CodeCopStub: CodeCop {
    // CodeCopStub declares a static value on the implementing type
    // that you can use to control what is returned by
    // `shouldAllowExecution()`.
    //
    // Note that this has to be static, because you can't add stored instance
    // variables in extensions.
    static var allowed: Bool { get }
}

然后扩展 CodeCopStubshouldAllowExecution() 方法,继承自 CodeCop,根据新的静态变量返回一个值 允许。这将覆盖任何实现 CodeCopStub 的类型的原始 CodeCop 实现。

extension CodeCopStub {
    func shouldAllowExecution() -> Bool {
        // We use `Self` here to refer to the implementing type (`Worker` in
        // this case).
        return Self.allowed
    }
}

此时您剩下要做的就是使 Worker 符合 CodeCopStub:

extension Worker: CodeCopStub {
    // It doesn't matter what the initial value of this variable is, because
    // you're going to set it in every test, but it has to have one because
    // it's static.
    static var allowed: Bool = false
}

您的测试将如下所示:

func testAllowed() {
    // Create the worker.
    let worker = Worker()
    // Because `Worker` has been extended to conform to `CodeCopStub`, it will
    // have this static property. Set it to true to cause
    // `shouldAllowExecution()` to return `true`.
    Worker.allowed = true

    // Call the method and get the result.
    let actualResult = worker.doSomeStuff()
    // Make sure the result was correct.
    let expectedResult = "Cop allowed it"
    XCTAssertEqual(expectedResult, actualResult)
}

func testNotAllowed() {
    // Same stuff as last time...
    let worker = Worker()
    // ...but you tell it not to allow it.
    Worker.allowed = false

    let actualResult = worker.doSomeStuff()
    // This time, the expected result is different.
    let expectedResult = "Cop said no"
    XCTAssertEqual(expectedResult, actualResult)
}

请记住,所有这些代码都应该放在您的测试目标中,而不是您的主要目标。通过将它放在您的测试目标中,它们都不会影响您的原始代码,并且不需要对原始代码进行修改。

关于swift - 如何在结构/类中 stub Swift "Trait/Mixin"方法进行测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48230545/

相关文章:

arrays - 在另一个 Swift 文件中使用返回值(字符串数组)

ios - Swift 2.2 enumerateObjectsUsingBlock - 停止

c# - 如何将空值从 CSV 文件传递​​给数据驱动的单元测试?

php - 如何避免冲突用于依赖注入(inject)的 PHP 特性

generics - 为什么在关联类型上不识别除第一个之外的 super 特征边界?

swift - iOS Swift 3 - 删除周年纪念日联系信息字段

.net - 使用 TFS 2010 运行异步任务单元测试

java - 如何离线使用 RestTemplate 对类进行单元测试?

function - 缺少顶级功能的实现

swift - Apple Watch 加速度计是否提供正确的数据?