我在哪里放置模拟代码?我需要再写一遍吗?我是否更改了原始代码?
答案 0 :(得分:2)
简短回答:使用协议。
如果您的injectable对象是final,struct或enum,则甚至无法将其覆盖为mock。而不是使用具体类型作为您的依赖项,使用协议并使您的实现符合它。除了允许“模拟”而不管实际类型(类,结构,枚举)之外,它还在一个地方列出了公共接口,不受实现的干扰。它还会迫使您考虑需要成为非私有界面的一部分。
使用追溯协议一致性(即在扩展中),您甚至可以使用它来模拟系统类,例如CBCentralManager
或CLLocationManager
。
示例:
不容易模仿:
struct Foo {
let id: Int
let data: Int
}
final class FooManager {
var x: Int
func getFoo(id: Int) -> Foo {
return Foo(id: id, data: x)
}
}
class FooUser {
let fooManager: FooManager
init(fooManager: FooManager) {
self.fooManager = fooManager
}
func getData() -> Int {
return fooManager.getFoo(id: 3).data
}
}
琐碎的模仿:
struct Foo {
let id: Int
let data: Int
}
// Easy to see what's visible.
protocol FooManager {
func getFoo(id: Int) -> Foo
}
final class RealFooManager: FooManager {
private var x: Int
func getFoo(id: Int) -> Foo {
return Foo(id: id, data: x)
}
}
class FooUser {
let fooManager: FooManager
init(fooManager: FooManager) {
self.fooManager = fooManager
}
func getData() -> Int {
return fooManager.getFoo(id: 3).data
}
}
// In test target.
class MockFooManager: FooManager {
var requestedId: Int?
var data: Int = 17
func getFoo(id: Int) -> Foo {
requestedId = id
return Foo(id, data: data)
}
}
class FooUserTests {
func testFooUserGetData() {
let mock = MockFooManager()
let user = FooUser(fooManager: mock)
let data = user.getData()
XCTAssertEqual(data, mock.data)
XCTAssertEqual(mock.requestedId, 3)
}
}