【发布时间】:2021-06-22 17:58:43
【问题描述】:
我有一个简单的类来存储和CurrentUser 到Realm 类型的实例,称为CurrentUserStore。此类中有一条规则,即一个对象始终只能有一条记录,因此对于 register(user:) 函数,在添加新用户之前需要先清除该类型的领域。代码如下:
final class CurrentUserStore: CurrentUserStoreProtocol {
private let realmAdapter: RealmAdapterInterface
init(realmAdapter: RealmAdapterInterface) {
self.realmAdapter = realmAdapter
}
func initiate() throws { ... }
func register(user: User) {
if !realmAdapter.retrieve(type: User.Type).isEmpty {
realmAdapter.clear(type: User.Type)
}
realmAdapter.add(user)
}
func retrieve() -> User { ... }
func unregister() { ... }
}
protocol RealmAdapterInterface {
func initiate() throws
func add<T: Any>(_ entry: T)
func retrieve<T: Any>(type: T.Type) -> [T]
func clear<T: Any>(type: T.Type)
}
现在,为了测试这种行为,我有这个:
class CurrentUserStoreTest: XCTestCase {
func testRegister_WhenAdapterRetrievesTooManyUsers_ShouldClearAdapterFirst() {
let realmAdapter = MockRealmAdapter(retrieveReturnValue: [User.random(), User.random()])
let store = CurrentUserStore(realmAdapter: realmAdapter)
let inputtedUser = User.random()
store.register(inputtedUser)
XCTAssertTrue(realmAdapter.isClearCalled)
XCTAssertTrue(realmAdapter.clearArgumentType is User.Type)
XCTAssertTrue(realmAdapter.isAddCalled)
XCTAssertEqual(inputtedUser, realmAdapter.addArgumentUser)
}
}
private final class MockRealmAdapter: RealmAdapterInterface {
private(set) var isInitiateCalled = false
private(set) var isAddCalled = false
private(set) var isRetrieveCalled = false
private(set) var isClearCalled = false
private(set) var initiateCallCount = 0
private(set) var addArgumentUser: Any?
private(set) var retrieveArgumentType: Any.Type?
private(set) var clearArgumentType: Any.Type?
private let retrieveReturnValue: [Any]
init(retrieveReturnValue: [Any] = []) {
self.retrieveReturnValue = retrieveReturnValue
}
func initiate() throws {
isInitiateCalled = true
initiateCallCount += 1
}
func add<T>(_ entry: T) throws {
isAddCalled = true
addArgumentUser = entry
}
func retrieve<T>(type: T.Type) throws -> [T] {
isRetrieveCalled = true
retrieveArgumentType = type
return retrieveReturnValue
}
func clear<T>(ype: T.Type) throws {
isClearCalled = true
clearArgumentType = type
}
}
现在,如果我要将 register(user:) 函数变成这样:
func register(user: User) {
if !realmAdapter.retrieve(type: User.Type).isEmpty {
realmAdapter.add(user)
realmAdapter.clear(type: User.Type)
} else {
realmAdapter.add(user)
}
}
测试仍然是绿色的,因为我们仍然会调用适配器类的add(_:) 和clear(type:) 函数。虽然因为执行的顺序,结果会很不一样。关于如何测试这个执行顺序的任何想法?
谢谢。
【问题讨论】:
-
如果调用的顺序很重要,你需要让你的 mock 跟踪调用的顺序。
-
@matt 你知道吗?你说得对。我怎么没想到。谢谢。
标签: ios swift unit-testing testing