Brightify / Cuckoo

Boilerplate-free mocking framework for Swift!
MIT License
1.67k stars 172 forks source link

How to expect an Objective-C property with Cuckoo and OCMock? #399

Open chrisjf opened 3 years ago

chrisjf commented 3 years ago

Hello, I'm new to Cuckoo and have a basic question. How can I properly expect that a property is called in an Objective-C class? Please find below the relevant files. Thanks for your help in advance!

//  Controller.swift

import Foundation

class Controller {

    let manager: Manager

    init(manager: Manager = Manager()) {
        self.manager = manager
    }

    func doSomething() {
        manager.shouldDisableAdIdentitySharing = true
    }

}
//  Manager.h

 #import <Foundation/Foundation.h> 

NS_ASSUME_NONNULL_BEGIN

@interface Manager: NSObject

@property (nonatomic, getter=shouldDisableAdIdentitySharing) BOOL disableAdIdentitySharing;

@end

NS_ASSUME_NONNULL_END
//  Manager.m

#import "Manager.h"

@implementation Manager

@end
//  CuckooSampleTests.swift

@testable import CuckooSample
import Cuckoo
import OCMock
import XCTest

class CuckooSampleTests: XCTestCase {

    func testExample() throws {
        let mockManager = objcStub(for: Manager.self) { stubber, mock in
//            stubber.when(mock.shouldDisableAdIdentitySharing).thenDoNothing()
        }

        let controller = Controller(manager: mockManager)

        controller.doSomething()

        objcVerify(mockManager.shouldDisableAdIdentitySharing)
    }

}
Screen Shot 2021-09-21 at 18 11 58

If Manager was a Swift class, I could just write something like when(stub.shouldDisableAdIdentitySharing.set(any())).thenDoNothing() but how can I do this for an Objective-C class?

MatyasKriz commented 2 years ago

Does the mock have the method setDisableAdIdentitySharing? It's been a while since we've implemented the Obj-C support, so it's possible that property setters can't be mocked this way. In that case, this needs to be implemented on top of the current functionality and I'm sorry that Cuckoo doesn't support this yet. 😕

chrisjf commented 2 years ago

Hey Matyas! Thanks for the reply.

Does the mock have the method setDisableAdIdentitySharing?

setDisableAdIdentitySharing is auto-synthesized by the compiler from the property disableAdIdentitySharing.

It's been a while since we've implemented the Obj-C support, so it's possible that property setters can't be mocked this way. In that case, this needs to be implemented on top of the current functionality and I'm sorry that Cuckoo doesn't support this yet.

Alright, thanks for the information. I'll close the issue as I understand now that it's not possible to mock Obj-C properties with Cuckoo/OCMock in Swift.

MatyasKriz commented 2 years ago

I'd prefer to keep it open to have something to think about when adding new features as this seems to be pretty useful to add. I'm not available much at the moment, but it's still better to let others know about this and to not let it get forgotten in issue history.

ajpallares commented 2 years ago

I've also faced this issue and there's a simple walkaround to mock Obj-C setters with the current implementation of Cuckoo:

when({ stub.shouldDisableAdIdentitySharing = false }()).thenDoNothing()

The problem with primitive data types in Obj-C is that we can't use the OCMArg.any() matcher as for reference types. Therefore, we need to mock the setters with the exact value set.

MatyasKriz commented 2 years ago

@ajpallares, that's a great find! Thanks for sharing, I'll have to add this case to the tests and the README so that it's documented, but it seems acceptable.