Closed Nickersoft closed 3 years ago
Thanks for reporting, a few additional questions:
given
or verify
)?At a high level, the issue is that it’s choosing the Objective-C overload for given
or verify
over the Swift variant. For example, this can happen if the block type doesn’t match the expected one:
// Fails
given(service.makeAPICall(endpoint: "https://my.com/endpoint", callback: any())).will {
/* no-op */
}
// Succeeds
given(service.makeAPICall(endpoint: "https://my.com/endpoint", callback: any())).will {
endpoint, callback in /* no-op */
}
This can also happen if you’re upgrading from an older version of Mockingbird which incorrectly mocked methods defined in extensions. For example:
// Fails in 0.18 but "succeeds" in 0.16 and is a compiler error in 0.17
given(service.makeAPICall(endpoint: "https://my.com/endpoint")).will {
/* will never be called */
}
// Succeeds
given(service.makeAPICall(endpoint: "https://my.com/endpoint", callback: any())).will {
endpoint, callback in /* no-op */
}
Overall I agree the error message here is confusing, but I’m not sure if there’s a great alternative outside of removing the overloads (and de-unifying the syntax for Objective-C and Swift).
Hey @andrewchang-bird! Thanks for the response... I upgraded from 0.16.0 to 0.18.1. The full call site is along the lines of:
given(service.makeAPICall(endpoint: "https://my.com/endpoint", callback: any())).willReturn(.success(MyObject()))
assuming the service returns an enum of { success(val), failure(err) }. I'd prefer finding a solution within my test suite itself... removing the overloads would effectively mean refactoring my entire codebase seeing all of the optional values would then become required, and my DI uses protocols to access all the injected services.
Thanks for the additional info. I’m having trouble repro’ing the error with the following:
/* Source */
enum ServiceResult {
case success(Bool)
case failure(Error)
}
protocol MyServicable {
func makeAPICall(endpoint: String, callback: (() -> Void)?) -> ServiceResult
}
extension MyServicable {
func makeAPICall(endpoint: String, callback: (() -> Void)? = nil) -> ServiceResult {
return self.makeAPICall(endpoint: endpoint, callback: callback)
}
}
/* Test */
func testProtocolExtensionAny() {
let service = mock(MyServicable.self)
given(service.makeAPICall(endpoint: "https://my.com/endpoint", callback: any()))
.willReturn(.success(true))
}
What happens if you manually cast the invocation to Mockable
? E.g.
given(service.makeAPICall(...) as Mockable).willReturn(...)
Hmm.. so I wasn't able to cast it because Mockable is generic and I wasn't sure what to use for type parameters. That said, I think I've uncovered the issue. In my case, my methods had multiple optional parameters in the signature, but I was only using any()
on one and omitting the others. This is why I think it was prioritizing the Objective-C signature.
So if my method was actually:
func makeAPICall(endpoint: String, arg1: String? = nil, arg2: String? = nil)
I was mocking it as:
given(service.makeAPICall(endpoint: "myendpoint.com", arg2: any())).willReturn(.success(MyObject())
Changing it to
given(service.makeAPICall(endpoint: "myendpoint.com", arg1: any(), arg2: any())).willReturn(.success(MyObject())
Fixes the issue. Just to clarify, when you said that it is choosing the Objective-C overload... I noticed in the generated mocks two signatures for my methods: one that uses Swift scalars, and one that uses @autoclosure () ->
. Is the @autoclosure
overload the Objective-C one?
At a high level, Mockingbird generates two methods for Swift testing:
given
and verify
in tests with @autoclosure
parameter types, which returns a Mockable
typeIn addition, the testing API itself has overloads for given
and verify
to support Objective-C mocking. For example, given
has:
Mockable
typeSo using any()
only for some parameters will route to the extension method which doesn’t return a Mockable
type, and hence pick the Objective-C given
overload.
Ah, makes sense! Cool, so I'm going to close this out seeing I managed to find a solution. Thanks for the help!
New Issue Checklist
Description
I recently upgraded my Mockingbird installation, and suddenly started receiving slews of the following error across my entire test suite when running my tests:
Almost every instance where this is occurring is a case in which the method has default parameters. For example, if the protocol is:
and it has the extension:
and it has being mocked as:
then this error is thrown. This wasn't happening with previous versions of Mockingbird, so I'm wondering what might have changed to cause this issue.
Framework Bugs
You should be able to write a reproduction case following the example above – if needed I can set up a separate repo to demonstrate.
Environment
mockingbird version
) 0.18.1swift --version
) 5.4.2.mockingbird-ignore
? No