在测试中模拟 UIView 扩展 "ApiProtocol" - swift4
Mock UIView extension "ApiProtocol" in test - swift4
给定 "injected" 到 MyUIView 的协议和扩展。
可以调用 func stubMePlease()
并且它 returns "hello world" 如预期的那样。
进行单元测试时 stubMePlease()
需要在 3 个不同的测试中进行模拟。
最好的方法是什么?
协议和扩展
protocol ApiProtocol {
func stubMePlease() -> String
}
extension ApiProtocol {
func stubMePlease() -> String {
return "hello world"
}
}
UIView"inject/extend"ApiProtocol
class MyUIView: UIView, ApiProtocol {
override init(frame: CGRect) {
super.init(frame: frame)
foo()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
public func foo() -> String {
return self.stubMePlease() // returns "hello world"
}
}
测试class
import XCTest
class test: XCTestCase {
var myview: MyUIView!
override func setUp() {
super.setUp()
myview = MyUIView()
}
func testA() {
let kom = myview.foo()
XCTAssertEqual(kom, "hello") // how do I mock this with "hello"
}
func testB() {
let kom = myview.foo()
XCTAssertEqual(kom, "weekend") // how do I mock this with "weekend"
}
func testC() {
let kom = myview.foo()
XCTAssertEqual(kom, "swift") // how do I mock this with "swift"
}
}
谢谢
您不应该使用 "extension" 来实现协议的功能,您应该使用它来 扩展 您的协议,即添加新功能。通常,您使用一些方便的方法重载来扩展您自己的协议,并根据协议中的方法来实现它们,以便每个实现相同协议的 class 都可以避免实现它们(同时客户端从重载中受益)。
首先,您需要实际实现您的协议:
class ApiProtocolForTests: ApiProtocol {
func stubMePlease() -> String {
return "fake hello world in test"
}
}
class ApiProtocolForRealz: ApiProtocol {
func stubMePlease() -> String {
return "hello world in real life"
}
}
然后在 MyUIView 而不是 inheriting/implementing ApiProtocol,这在概念上没有意义,你应该在你的 init 构造函数中传递它,例如:
class MyUIView: UIView {
let api: ApiProtocol
override init(frame: CGRect, api: ApiProtocol) {
super.init(frame: frame)
self.api = api
foo()
}
...
在测试中,您将通过提供协议的 ApiProtocolForTests 版本来创建 MyUIView:myview = MyUIView(frame: frame, api: ApiProtocolForTests())
这也称为 "injecting" a dependency "ApiProtocol"。
给定 "injected" 到 MyUIView 的协议和扩展。
可以调用 func stubMePlease()
并且它 returns "hello world" 如预期的那样。
进行单元测试时 stubMePlease()
需要在 3 个不同的测试中进行模拟。
最好的方法是什么?
协议和扩展
protocol ApiProtocol {
func stubMePlease() -> String
}
extension ApiProtocol {
func stubMePlease() -> String {
return "hello world"
}
}
UIView"inject/extend"ApiProtocol
class MyUIView: UIView, ApiProtocol {
override init(frame: CGRect) {
super.init(frame: frame)
foo()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
public func foo() -> String {
return self.stubMePlease() // returns "hello world"
}
}
测试class
import XCTest
class test: XCTestCase {
var myview: MyUIView!
override func setUp() {
super.setUp()
myview = MyUIView()
}
func testA() {
let kom = myview.foo()
XCTAssertEqual(kom, "hello") // how do I mock this with "hello"
}
func testB() {
let kom = myview.foo()
XCTAssertEqual(kom, "weekend") // how do I mock this with "weekend"
}
func testC() {
let kom = myview.foo()
XCTAssertEqual(kom, "swift") // how do I mock this with "swift"
}
}
谢谢
您不应该使用 "extension" 来实现协议的功能,您应该使用它来 扩展 您的协议,即添加新功能。通常,您使用一些方便的方法重载来扩展您自己的协议,并根据协议中的方法来实现它们,以便每个实现相同协议的 class 都可以避免实现它们(同时客户端从重载中受益)。
首先,您需要实际实现您的协议:
class ApiProtocolForTests: ApiProtocol {
func stubMePlease() -> String {
return "fake hello world in test"
}
}
class ApiProtocolForRealz: ApiProtocol {
func stubMePlease() -> String {
return "hello world in real life"
}
}
然后在 MyUIView 而不是 inheriting/implementing ApiProtocol,这在概念上没有意义,你应该在你的 init 构造函数中传递它,例如:
class MyUIView: UIView {
let api: ApiProtocol
override init(frame: CGRect, api: ApiProtocol) {
super.init(frame: frame)
self.api = api
foo()
}
...
在测试中,您将通过提供协议的 ApiProtocolForTests 版本来创建 MyUIView:myview = MyUIView(frame: frame, api: ApiProtocolForTests())
这也称为 "injecting" a dependency "ApiProtocol"。