Generated mocks "Unable to infer complex closure return type"
daniel-beard opened this issue · 4 comments
New Issue Checklist
- I updated the framework and generator to the latest version
- I searched the existing GitHub issues and list of common problems
Overview
Generated mocks are not able to be compiled in some cases:
Unable to infer complex closure return type; add explicit type to disambiguate
The method that is being mocked here is removeOverrides()
which is a public func on a public class, that conforms to a protocol (of which removeOverrides()
is a member)
Example
public protocol SomeProtocol {
func removeOverrides()
}
public class MyClass: SomeProtocol {
public func removeOverrides() {
UserDefaults.standard.removeObject(forKey: overridesKey)
}
}
A minimal example of the code being tested along with the test case.
Expected Behavior
Should be able to compile the generated mocks
Environment
- Mockingbird CLI version (
mockingbird version
)
0.19.2 - Xcode and Swift version (
swift --version
)
swift-driver version: 1.26.21 Apple Swift version 5.5.2 (swiftlang-1300.0.47.5 clang-1300.0.29.30) - Package manager (CocoaPods, Carthage, SPM project, SPM package)
SwiftPM - Unit testing framework (XCTest, Quick/Nimble)
XCTest
Thanks for reporting, Daniel. I’m not able to repro this with the provided example, but I noticed that the generated code in your screenshot looks different from the current output of 0.19.2:
Could you provide additional context or the full output, assuming that the generated code was modified by hand?
The example was simplified as I'm not able to provide the original code. The output was not modified, here's the full generated file:
DevModeMocks.generated.swift
//
// DevModeMocks.generated.swift
// DevMode
//
// Generated by Mockingbird v0.19.2.
// DO NOT EDIT
//
import Combine
@testable import DevMode
import Foundation
@testable import Mockingbird
import Swift
import SwiftUI
private let genericStaticMockContext = Mockingbird.GenericStaticMockContext()
// MARK: - Mocked ExperimentsProviding
public final class ExperimentsProvidingMock: DevMode.ExperimentsProviding, Mockingbird.Mock {
typealias MockingbirdSupertype = DevMode.ExperimentsProviding
static let staticMock = Mockingbird.StaticMock()
public let mockingbirdContext = Mockingbird.Context(["generator_version": "0.19.2", "module_name": "DevMode"])
fileprivate init(sourceLocation: Mockingbird.SourceLocation) {
self.mockingbirdContext.sourceLocation = sourceLocation
ExperimentsProvidingMock.staticMock.mockingbirdContext.sourceLocation = sourceLocation
}
// MARK: Mocked `catfoodOverride`(`experimentID`: String)
public func catfoodOverride(experimentID: String) -> DevMode.ExperimentCatfoodData {
self.mockingbirdContext.mocking.didInvoke(Mockingbird.SwiftInvocation(selectorName: "`catfoodOverride`(`experimentID`: String) -> DevMode.ExperimentCatfoodData", selectorType: Mockingbird.SelectorType.method, arguments: [Mockingbird.ArgumentMatcher(experimentID)], returnType: Swift.ObjectIdentifier((DevMode.ExperimentCatfoodData).self))) {
self.mockingbirdContext.recordInvocation($0)
let mkbImpl = self.mockingbirdContext.stubbing.implementation(for: $0)
if let mkbImpl = mkbImpl as? (String) -> DevMode.ExperimentCatfoodData { return mkbImpl(experimentID) }
if let mkbImpl = mkbImpl as? () -> DevMode.ExperimentCatfoodData { return mkbImpl() }
for mkbTargetBox in self.mockingbirdContext.proxy.targets(for: $0) {
switch mkbTargetBox.target {
case .super:
break
case let .object(mkbObject):
guard var mkbObject = mkbObject as? MockingbirdSupertype else { break }
let mkbValue: DevMode.ExperimentCatfoodData = mkbObject.catfoodOverride(experimentID: experimentID)
self.mockingbirdContext.proxy.updateTarget(&mkbObject, in: mkbTargetBox)
return mkbValue
}
}
if let mkbValue = self.mockingbirdContext.stubbing.defaultValueProvider.value.provideValue(for: (DevMode.ExperimentCatfoodData).self) { return mkbValue }
self.mockingbirdContext.stubbing.failTest(for: $0, at: self.mockingbirdContext.sourceLocation)
}
}
public func catfoodOverride(experimentID: @autoclosure () -> String) -> Mockingbird.Mockable<Mockingbird.FunctionDeclaration, (String) -> DevMode.ExperimentCatfoodData, DevMode.ExperimentCatfoodData> {
Mockingbird.Mockable<Mockingbird.FunctionDeclaration, (String) -> DevMode.ExperimentCatfoodData, DevMode.ExperimentCatfoodData>(mock: self, invocation: Mockingbird.SwiftInvocation(selectorName: "`catfoodOverride`(`experimentID`: String) -> DevMode.ExperimentCatfoodData", selectorType: Mockingbird.SelectorType.method, arguments: [Mockingbird.resolve(experimentID)], returnType: Swift.ObjectIdentifier((DevMode.ExperimentCatfoodData).self)))
}
// MARK: Mocked `removeOverrides`()
public func removeOverrides() {
self.mockingbirdContext.mocking.didInvoke(Mockingbird.SwiftInvocation(selectorName: "`removeOverrides`() -> Void", selectorType: Mockingbird.SelectorType.method, arguments: [], returnType: Swift.ObjectIdentifier(Void.self))) {
self.mockingbirdContext.recordInvocation($0)
let mkbImpl = self.mockingbirdContext.stubbing.implementation(for: $0)
if let mkbImpl = mkbImpl as? () -> Void { return mkbImpl() }
for mkbTargetBox in self.mockingbirdContext.proxy.targets(for: $0) {
switch mkbTargetBox.target {
case .super:
break
case let .object(mkbObject):
guard var mkbObject = mkbObject as? MockingbirdSupertype else { break }
let mkbValue: Void = mkbObject.removeOverrides()
self.mockingbirdContext.proxy.updateTarget(&mkbObject, in: mkbTargetBox)
return mkbValue
}
}
if let mkbValue = self.mockingbirdContext.stubbing.defaultValueProvider.value.provideValue(for: Void.self) { return mkbValue }
self.mockingbirdContext.stubbing.failTest(for: $0, at: self.mockingbirdContext.sourceLocation)
}
}
public func removeOverrides() -> Mockingbird.Mockable<Mockingbird.FunctionDeclaration, () -> Void, Void> {
Mockingbird.Mockable<Mockingbird.FunctionDeclaration, () -> Void, Void>(mock: self, invocation: Mockingbird.SwiftInvocation(selectorName: "`removeOverrides`() -> Void", selectorType: Mockingbird.SelectorType.method, arguments: [], returnType: Swift.ObjectIdentifier(Void.self)))
}
// MARK: Mocked `save`(`experiments`: [DevMode.Experiment])
public func save(experiments: [DevMode.Experiment]) {
self.mockingbirdContext.mocking.didInvoke(Mockingbird.SwiftInvocation(selectorName: "`save`(`experiments`: [DevMode.Experiment]) -> Void", selectorType: Mockingbird.SelectorType.method, arguments: [Mockingbird.ArgumentMatcher(experiments)], returnType: Swift.ObjectIdentifier(Void.self))) {
self.mockingbirdContext.recordInvocation($0)
let mkbImpl = self.mockingbirdContext.stubbing.implementation(for: $0)
if let mkbImpl = mkbImpl as? ([DevMode.Experiment]) -> Void { return mkbImpl(experiments) }
if let mkbImpl = mkbImpl as? () -> Void { return mkbImpl() }
for mkbTargetBox in self.mockingbirdContext.proxy.targets(for: $0) {
switch mkbTargetBox.target {
case .super:
break
case let .object(mkbObject):
guard var mkbObject = mkbObject as? MockingbirdSupertype else { break }
let mkbValue: Void = mkbObject.save(experiments: experiments)
self.mockingbirdContext.proxy.updateTarget(&mkbObject, in: mkbTargetBox)
return mkbValue
}
}
if let mkbValue = self.mockingbirdContext.stubbing.defaultValueProvider.value.provideValue(for: Void.self) { return mkbValue }
self.mockingbirdContext.stubbing.failTest(for: $0, at: self.mockingbirdContext.sourceLocation)
}
}
public func save(experiments: @autoclosure () -> [DevMode.Experiment]) -> Mockingbird.Mockable<Mockingbird.FunctionDeclaration, ([DevMode.Experiment]) -> Void, Void> {
Mockingbird.Mockable<Mockingbird.FunctionDeclaration, ([DevMode.Experiment]) -> Void, Void>(mock: self, invocation: Mockingbird.SwiftInvocation(selectorName: "`save`(`experiments`: [DevMode.Experiment]) -> Void", selectorType: Mockingbird.SelectorType.method, arguments: [Mockingbird.resolve(experiments)], returnType: Swift.ObjectIdentifier(Void.self)))
}
// MARK: Mocked `fetchExperiments`()
public func fetchExperiments() -> [DevMode.Experiment] {
self.mockingbirdContext.mocking.didInvoke(Mockingbird.SwiftInvocation(selectorName: "`fetchExperiments`() -> [DevMode.Experiment]", selectorType: Mockingbird.SelectorType.method, arguments: [], returnType: Swift.ObjectIdentifier([DevMode.Experiment].self))) {
self.mockingbirdContext.recordInvocation($0)
let mkbImpl = self.mockingbirdContext.stubbing.implementation(for: $0)
if let mkbImpl = mkbImpl as? () -> [DevMode.Experiment] { return mkbImpl() }
for mkbTargetBox in self.mockingbirdContext.proxy.targets(for: $0) {
switch mkbTargetBox.target {
case .super:
break
case let .object(mkbObject):
guard var mkbObject = mkbObject as? MockingbirdSupertype else { break }
let mkbValue: [DevMode.Experiment] = mkbObject.fetchExperiments()
self.mockingbirdContext.proxy.updateTarget(&mkbObject, in: mkbTargetBox)
return mkbValue
}
}
if let mkbValue = self.mockingbirdContext.stubbing.defaultValueProvider.value.provideValue(for: [DevMode.Experiment].self) { return mkbValue }
self.mockingbirdContext.stubbing.failTest(for: $0, at: self.mockingbirdContext.sourceLocation)
}
}
public func fetchExperiments() -> Mockingbird.Mockable<Mockingbird.FunctionDeclaration, () -> [DevMode.Experiment], [DevMode.Experiment]> {
Mockingbird.Mockable<Mockingbird.FunctionDeclaration, () -> [DevMode.Experiment], [DevMode.Experiment]>(mock: self, invocation: Mockingbird.SwiftInvocation(selectorName: "`fetchExperiments`() -> [DevMode.Experiment]", selectorType: Mockingbird.SelectorType.method, arguments: [], returnType: Swift.ObjectIdentifier([DevMode.Experiment].self)))
}
}
/// Returns a concrete mock of `ExperimentsProviding`.
public func mock(_ type: DevMode.ExperimentsProviding.Protocol, file: StaticString = #file, line: UInt = #line) -> ExperimentsProvidingMock {
ExperimentsProvidingMock(sourceLocation: Mockingbird.SourceLocation(file, line))
}
Thanks for the full file. Based on the output it looks like a code formatter is being run on the output, maybe SwiftLint or swift-format
. If it’s SwiftLint, you can specify --disable-swiftlint
as a generator flag to disable SwiftLint rules on mock files. If it’s swift-format
, you should configure your project to not run the tool against *.generated.swift
files.
Thanks, it was swift format. You can consider this resolved.