子类化 Typhoon 程序集以覆盖不起作用的方法

Subclassing a Typhoon assembly to override methods not working

我正在对 Typhoon 程序集进行子class,以便 return 对存根实现进行单元测试。

我的程序集看起来像这样:

class RealAssembly : TyphoonAssembly {
  public dynamic func instanceToStubOut() -> AnyObject {
    return TyphoonDefinition.withClass(SomeRealWorldClass.self)
  }

  public dynamic func instanceToTest() -> AnyObject {
    return TyphoonDefinition.withClass(ClassToTest.self, configuration: { (definition : TyphoonDefinition!) -> Void in 
      definition.useInitializer("initWithObjectToStub:", parameters: { (initializer : TyphoonMethod!) -> Void in
        initializer.injectParameterWith(self.instancetoStubOut())
      })
    })
  }
}

我的测试 class 仅用于测试类型 ClassToTest 的实例,我想使用对类型 SomeRealWorldClass 的对象的初始化程序注入依赖项进行测试被淘汰了。所以我 subclass RealAssembly 以便 instanceToStubOut() 被覆盖到 return 我的存根对象。

class MyTestClass : XCTestCase {
  var assembly : TestAssembly!

  class TestAssembly : RealAssembly {
    override dynamic func instanceToStubOut() -> AnyObject {
      return TyphoonDefinition.withClass(TestClass.self)
    }
  }

  @objc
  class TestClass : NSObject, ClassToStubOut {
    func methodToStubOut() { /* do nothing */ }
  }

  override func setUp() {
    super.setUp()
    self.assembly = TestAssembly().activate()
  }

  override func tearDown() {
    self.assembly = nil
    super.tearDown()
  }

  func testStuff() {
    let testingInstance = self.assembly.instanceToTest()
    XCTAssertTrue(testingInstance.doStuff(), "doStuff returns true")
  }
}

我原以为这会起作用,但它不起作用。台风似乎注入了一个未初始化的对象,而不是调用 TestAssembly.instanceToStubOut()

我是不是做错了什么?我应该采取不同的方法吗?

编辑:这里有一些代码,您可以将其粘贴到演示问题的 Swift 游乐场中。最后一行显示 c.otherInstance returning nil :

import Typhoon

@objc
class BaseClass : NSObject {
    var otherInstance : OtherProtocol!

    func doIt() -> String {
        return self.otherInstance.doStuff()
    }
}

@objc
protocol OtherProtocol {
    func doStuff() -> String
}

@objc
class OtherImpl : NSObject, OtherProtocol {
    func doStuff() -> String {
        return "OtherClass"
    }
}

@objc
class StubClass : NSObject, OtherProtocol {
    func doStuff() -> String {
        return "Stubbed out"
    }
}

class BaseAssembly : TyphoonAssembly {
    dynamic func baseObject() -> AnyObject {
        return TyphoonDefinition.withClass(BaseClass.self,
            configuration: { (def : TyphoonDefinition!) -> Void in
            def.injectProperty("otherInstance", with: self.otherObject())
        })
    }

    dynamic func otherObject() -> AnyObject {
        return TyphoonDefinition.withClass(OtherImpl.self)
    }
}

var assembly = BaseAssembly()
assembly.activate()
var b = assembly.baseObject() as! BaseClass
b.doIt()

@objc
class TestAssembly : BaseAssembly {
    override func otherObject() -> AnyObject {
        return TyphoonDefinition.withClass(StubClass.self)
    }
}

var testAssembly = TestAssembly()
testAssembly.activate()
var c = testAssembly.baseObject() as! BaseClass
c.otherInstance // this shouldn't be nil

编辑:

虽然打补丁是一种选择,如下面@Herman 的回答所述,问题中尝试的是受支持的功能,但是存在一个回归错误,无法正常工作。

回归错误已在 Typhoon 3.2.2 中修复,因此现在修补和覆盖程序集再次成为针对特定用例配置 Typhoon 的选项。

正在修补

Typhoon 中有一个用于此目的的修补功能。看here
例如:

class StubClass : NSObject, OtherProtocol {
    @objc func doStuff() -> String {
        return "Stubbed out"
    }
}

let assembly = BaseAssembly()
assembly.activate()
let b = assembly.baseObject() as! BaseClass
print(b.doIt())

let testAssembly = BaseAssembly().activate()
let patcher = TyphoonPatcher()
patcher.patchDefinitionWithSelector("otherObject") { () -> AnyObject! in
    return StubClass()
}

testAssembly.attachPostProcessor(patcher)
let c = testAssembly.baseObject() as! BaseClass
print(c.doIt())