jeudi 25 mars 2021

How to test for order of execution

I've got this simple class to store and instance of the type CurrentUser to Realm called CurrentUserStore. There is one rule in this class, that is there can only be one record for an object at all times so for the register(user:) func will need to clear the Realm for that type first before adding a new user. Here's the code:

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)
}

Now, to test this behaviour, I have this:

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 retrieveOutputEntity: [Any]

    init(retrieveReturnValue: [Any] = []) {
        self.retrieveOutputEntity = retrieveOutputEntity
    }
    
    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
    }
    
    func clear<T>(ype: T.Type) throws {
        isClearCalled = true
        clearArgumentType = type
    }
}

Now, if I were to make the register(user:) func into this:

    func register(user: User) {        
        if !realmAdapter.retrieve(type: User.Type).isEmpty {
            realmAdapter.add(user)
            realmAdapter.clear(type: User.Type)
        } else {
            realmAdapter.add(user)
        }
    }

The test will still be green because we will still call add(_:) and clear(type:) funcs of the adapter class. Although because of the order of execution, the result will be very different. Any idea about how to test this order of execution?

Thanks.

Aucun commentaire:

Enregistrer un commentaire