Brightify / Cuckoo

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

Mocks with protocols restricted to a explicit class #376

Open geraldeersteling opened 3 years ago

geraldeersteling commented 3 years ago

Hello,

I'm fairly new with Cuckoo and got everything up and running. I do encounter an issue and I'm unable to find a solution in the issues here on GitHub (or I'm not searching for it the right way...).

Take this protocol for example:

protocol Slide: UIVIew {
    var delegate: SlideDelegate? { get set }
}

Now when I run Cuckoo this gave me the following generated code:

Generated code ```swift class MockSlideDelegate: SlideDelegate, Cuckoo.ProtocolMock { typealias MocksType = SlideDelegate typealias Stubbing = __StubbingProxy_SlideDelegate typealias Verification = __VerificationProxy_SlideDelegate let cuckoo_manager = Cuckoo.MockManager.preconfiguredManager ?? Cuckoo.MockManager(hasParent: false) private var __defaultImplStub: SlideDelegate? func enableDefaultImplementation(_ stub: SlideDelegate) { __defaultImplStub = stub cuckoo_manager.enableDefaultStubImplementation() } func nextPressed(forSlide: Slide) { return cuckoo_manager.call("nextPressed(forSlide: Slide)", parameters: (forSlide), escapingParameters: (forSlide), superclassCall: Cuckoo.MockManager.crashOnProtocolSuperclassCall() , defaultCall: __defaultImplStub!.nextPressed(forSlide: forSlide)) } func previousPressed(forSlide: Slide) { return cuckoo_manager.call("previousPressed(forSlide: Slide)", parameters: (forSlide), escapingParameters: (forSlide), superclassCall: Cuckoo.MockManager.crashOnProtocolSuperclassCall() , defaultCall: __defaultImplStub!.previousPressed(forSlide: forSlide)) } struct __StubbingProxy_SlideDelegate: Cuckoo.StubbingProxy { private let cuckoo_manager: Cuckoo.MockManager init(manager: Cuckoo.MockManager) { self.cuckoo_manager = manager } func nextPressed(forSlide: M1) -> Cuckoo.ProtocolStubNoReturnFunction<(Slide)> where M1.MatchedType == Slide { let matchers: [Cuckoo.ParameterMatcher<(Slide)>] = [wrap(matchable: forSlide) { $0 }] return .init(stub: cuckoo_manager.createStub(for: MockSlideDelegate.self, method: "nextPressed(forSlide: Slide)", parameterMatchers: matchers)) } func previousPressed(forSlide: M1) -> Cuckoo.ProtocolStubNoReturnFunction<(Slide)> where M1.MatchedType == Slide { let matchers: [Cuckoo.ParameterMatcher<(Slide)>] = [wrap(matchable: forSlide) { $0 }] return .init(stub: cuckoo_manager.createStub(for: MockSlideDelegate.self, method: "previousPressed(forSlide: Slide)", parameterMatchers: matchers)) } } struct __VerificationProxy_SlideDelegate: Cuckoo.VerificationProxy { private let cuckoo_manager: Cuckoo.MockManager private let callMatcher: Cuckoo.CallMatcher private let sourceLocation: Cuckoo.SourceLocation init(manager: Cuckoo.MockManager, callMatcher: Cuckoo.CallMatcher, sourceLocation: Cuckoo.SourceLocation) { self.cuckoo_manager = manager self.callMatcher = callMatcher self.sourceLocation = sourceLocation } @discardableResult func nextPressed(forSlide: M1) -> Cuckoo.__DoNotUse<(Slide), Void> where M1.MatchedType == Slide { let matchers: [Cuckoo.ParameterMatcher<(Slide)>] = [wrap(matchable: forSlide) { $0 }] return cuckoo_manager.verify("nextPressed(forSlide: Slide)", callMatcher: callMatcher, parameterMatchers: matchers, sourceLocation: sourceLocation) } @discardableResult func previousPressed(forSlide: M1) -> Cuckoo.__DoNotUse<(Slide), Void> where M1.MatchedType == Slide { let matchers: [Cuckoo.ParameterMatcher<(Slide)>] = [wrap(matchable: forSlide) { $0 }] return cuckoo_manager.verify("previousPressed(forSlide: Slide)", callMatcher: callMatcher, parameterMatchers: matchers, sourceLocation: sourceLocation) } } } class SlideDelegateStub: SlideDelegate { func nextPressed(forSlide: Slide) { return DefaultValueRegistry.defaultValue(for: (Void).self) } func previousPressed(forSlide: Slide) { return DefaultValueRegistry.defaultValue(for: (Void).self) } } class MockSlide: Slide, Cuckoo.ProtocolMock { typealias MocksType = Slide typealias Stubbing = __StubbingProxy_Slide typealias Verification = __VerificationProxy_Slide let cuckoo_manager = Cuckoo.MockManager.preconfiguredManager ?? Cuckoo.MockManager(hasParent: false) private var __defaultImplStub: Slide? func enableDefaultImplementation(_ stub: Slide) { __defaultImplStub = stub cuckoo_manager.enableDefaultStubImplementation() } var delegate: SlideDelegate? { get { return cuckoo_manager.getter("delegate", superclassCall: Cuckoo.MockManager.crashOnProtocolSuperclassCall() , defaultCall: __defaultImplStub!.delegate) } set { cuckoo_manager.setter("delegate", value: newValue, superclassCall: Cuckoo.MockManager.crashOnProtocolSuperclassCall() , defaultCall: __defaultImplStub!.delegate = newValue) } } struct __StubbingProxy_Slide: Cuckoo.StubbingProxy { private let cuckoo_manager: Cuckoo.MockManager init(manager: Cuckoo.MockManager) { self.cuckoo_manager = manager } var delegate: Cuckoo.ProtocolToBeStubbedOptionalProperty { return .init(manager: cuckoo_manager, name: "delegate") } } struct __VerificationProxy_Slide: Cuckoo.VerificationProxy { private let cuckoo_manager: Cuckoo.MockManager private let callMatcher: Cuckoo.CallMatcher private let sourceLocation: Cuckoo.SourceLocation init(manager: Cuckoo.MockManager, callMatcher: Cuckoo.CallMatcher, sourceLocation: Cuckoo.SourceLocation) { self.cuckoo_manager = manager self.callMatcher = callMatcher self.sourceLocation = sourceLocation } var delegate: Cuckoo.VerifyOptionalProperty { return .init(manager: cuckoo_manager, name: "delegate", callMatcher: callMatcher, sourceLocation: sourceLocation) } } } class SlideStub: Slide { var delegate: SlideDelegate? { get { return DefaultValueRegistry.defaultValue(for: (SlideDelegate?).self) } set { } } } ```

However the generated mocks are no UIView's rather just classes which conform to the Slide protocol. Subsequently giving me the following errors while compiling:


How can I fix this, or is this 'just how things work'?

MatyasKriz commented 3 years ago

Hi, @geraldeersteling. Cuckoo needs the source code to parse it and find out which methods are declared, which it can't do from just the type unfortunately. The OCMock integration in Cuckoo might help with this, take a look at the tests and try it out.

bspinner commented 3 years ago

This is as well a problem with opensource classes/protocols:

public class ClassExample {
    @Published private(set) public var greetings: String = "Hello, World!"
}

public protocol ProtocolExample: ClassExample {
}

public class Example: ClassExample, ProtocolExample {

}

will generate

//
// ERROR: 'ProtocolExample' requires that 'MockProtocolExample' inherit from 'ClassExample'
//
public class MockProtocolExample: ProtocolExample, Cuckoo.ProtocolMock { 
// ...
    public var greetings: String {
        get {
            return cuckoo_manager.getter("greetings",
                superclassCall:

                    Cuckoo.MockManager.crashOnProtocolSuperclassCall()
                    ,
                defaultCall: __defaultImplStub!.greetings)
        }

    }

// ...

    public struct __StubbingProxy_ProtocolExample: Cuckoo.StubbingProxy {
        private let cuckoo_manager: Cuckoo.MockManager

        public init(manager: Cuckoo.MockManager) {
            self.cuckoo_manager = manager
        }

        //
        // ERROR: Type 'MockProtocolExample' does not conform to protocol 'ClassMock'
        //
        var greetings: Cuckoo.ClassToBeStubbedReadOnlyProperty<MockProtocolExample, String> {
            return .init(manager: cuckoo_manager, name: "greetings")
        }

    }
}

(see two errors in code comments)

MatyasKriz commented 3 years ago

@bspinner does the example work without the @Published?