How to use MethodTemplate class

Best Mockingbird code snippet using .MethodTemplate

Run Mockingbird automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Config.swift

Source: Config.swift Github

copy
1struct Config {
2  let source: String
3  let templatesPath: String
4  let typeTemplate: String
5  let typeSaveTo: String
6  let methodTemplate: String
7  let methodSaveTo: String
8}
9
10let CONFIG = Config(
11  source: "https://ark0f.github.io/tg-bot-api/custom.min.json", templatesPath: "templates",
12  typeTemplate: "Type.swift", typeSaveTo: "generated/types", methodTemplate: "Method.swift",
13  methodSaveTo: "generated/methods")
14
15let SKIPPED_OBJECTS: [String] = [
16  "Update",
17  "Message",
18  "Chat",
19  "ChatMember",
20  "InlineQueryResult",
21  "InputFile",
22  "KeyboardButtonPollType",
23  "EncryptedPassportElement",
24]
25
26let SKIPPED_METHODS: [String] = [
27
28]
29
Full Screen

MockableTypeTemplate.swift

Source: MockableTypeTemplate.swift Github

copy
1//
2//  MockGenerator.swift
3//  MockingbirdCli
4//
5//  Created by Andrew Chang on 8/6/19.
6//  Copyright © 2019 Bird Rides, Inc. All rights reserved.
7//
8
9// swiftlint:disable leading_whitespace
10
11import Foundation
12
13enum Declaration: String, CustomStringConvertible {
14  case functionDeclaration = "Mockingbird.FunctionDeclaration"
15  case throwingFunctionDeclaration = "Mockingbird.ThrowingFunctionDeclaration"
16  
17  case propertyGetterDeclaration = "Mockingbird.PropertyGetterDeclaration"
18  case propertySetterDeclaration = "Mockingbird.PropertySetterDeclaration"
19  
20  case subscriptGetterDeclaration = "Mockingbird.SubscriptGetterDeclaration"
21  case subscriptSetterDeclaration = "Mockingbird.SubscriptSetterDeclaration"
22  
23  var description: String { return rawValue }
24}
25
26extension GenericType {
27  var flattenedDeclaration: String {
28    guard !constraints.isEmpty else { return name }
29    let flattenedInheritedTypes = String(list: constraints.sorted(), separator: " & ")
30    return "\(name): \(flattenedInheritedTypes)"
31  }
32}
33
34extension MockableType {
35  func isReferenced(by typeNames: Set<String>) -> Bool {
36    return typeNames.contains(fullyQualifiedName.removingGenericTyping())
37        || typeNames.contains(fullyQualifiedModuleName.removingGenericTyping())
38  }
39}
40
41/// Renders a `MockableType` to a `PartialFileContent` object.
42class MockableTypeTemplate: Template {
43  let mockableType: MockableType
44  let mockedTypeNames: Set<String>?
45  
46  enum Constants {
47    static let mockProtocolName = "Mockingbird.Mock"
48    static let thunkStub = #"fatalError("See 'Thunk Pruning' in the README")"#
49  }
50  
51  private var methodTemplates = [Method: MethodTemplate]()
52  init(mockableType: MockableType, mockedTypeNames: Set<String>?) {
53    self.mockableType = mockableType
54    self.mockedTypeNames = mockedTypeNames
55  }
56  
57  func methodTemplate(for method: Method) -> MethodTemplate {
58    if let existing = methodTemplates[method] { return existing }
59    let template: MethodTemplate
60    if method.isInitializer {
61      template = InitializerMethodTemplate(method: method, context: self)
62    } else if method.kind == .functionSubscript {
63      template = SubscriptMethodTemplate(method: method, context: self)
64    } else {
65      template = MethodTemplate(method: method, context: self)
66    }
67    methodTemplates[method] = template
68    return template
69  }
70  
71  func render() -> String {
72    let (directiveStart, directiveEnd) = compilationDirectiveDeclaration
73    return String(lines: [
74      "// MARK: - Mocked \(mockableType.name)",
75      directiveStart,
76      NominalTypeDefinitionTemplate(
77        declaration: "public final class \(mockableType.name)Mock",
78        genericTypes: genericTypes,
79        genericConstraints: mockableType.whereClauses.sorted().map({ specializeTypeName("\($0)") }),
80        inheritedTypes: (isAvailable ? inheritedTypes : []) + [Constants.mockProtocolName],
81        body: renderBody()).render(),
82      directiveEnd,
83    ])
84  }
85  
86  lazy var compilationDirectiveDeclaration: (start: String, end: String) = {
87    guard !mockableType.compilationDirectives.isEmpty else { return ("", "") }
88    let start = String(lines: mockableType.compilationDirectives.map({ $0.declaration }))
89    let end = String(lines: mockableType.compilationDirectives.map({ _ in "#endif" }))
90    return (start, end)
91  }()
92  
93  lazy var shouldGenerateThunks: Bool = {
94    guard let typeNames = mockedTypeNames else { return true }
95    return mockableType.isReferenced(by: typeNames)
96  }()
97  
98  lazy var isAvailable: Bool = {
99    return unavailableMockAttribute.isEmpty
100  }()
101  
102  /// Protocols that inherit from opaque external types are considered not available as mocks
103  /// because it's not possible to generate a well-formed concrete implementation unless the
104  /// inheritance is trivial.
105  var nonMockableOpaqueInheritanceMessage: String? {
106    let hasCompleteInheritance = mockableType.kind != .protocol
107      || mockableType.opaqueInheritedTypeNames.isEmpty
108    guard !hasCompleteInheritance else { return nil }
109    
110    let opaqueTypeNames = mockableType.opaqueInheritedTypeNames.sorted()
111    let allOpaqueTypeNames = opaqueTypeNames.enumerated().map({ (index, typeName) -> String in
112      (index > 0 && index == opaqueTypeNames.count-1 ? "and " : "") + typeName.singleQuoted
113    }).joined(separator: opaqueTypeNames.count > 2 ? ", " : " ")
114    
115    if opaqueTypeNames.count > 1 {
116      return "\(mockableType.name.singleQuoted) inherits from the externally-defined types \(allOpaqueTypeNames) which needs to be declared in a supporting source file"
117    } else {
118      return "\(mockableType.name.singleQuoted) inherits from the externally-defined type \(allOpaqueTypeNames) which needs to be declared in a supporting source file"
119    }
120  }
121  
122  /// Cannot mock a type that inherits a type that isn't declared in the same module and isn't open.
123  /// This mainly applies to protocols that have a Self constraint to a non-inheritable type.
124  var nonMockableInheritedTypeMessage: String? {
125    guard let nonInheritableType = mockableType.inheritedTypes
126      .union(mockableType.selfConformanceTypes)
127      .first(where: {
128        $0.kind == .class && !$0.accessLevel.isInheritableType(withinSameModule: $0.shouldMock)
129      })
130      else { return nil }
131    return "\(mockableType.name.singleQuoted) inherits from the non-open class \(nonInheritableType.name.singleQuoted) and cannot be mocked"
132  }
133  
134  /// Protocols can inherit properties whose names conflict from other protocols, such that it is
135  /// not possible to create a class that conforms to the child protocol.
136  var nonMockablePropertiesMessage: String? {
137    let duplicates = Dictionary(grouping: mockableType.variables, by: {Variable.Reduced(from: $0)})
138      .mapValues({ $0.count })
139      .filter({ $1 > 1 })
140    guard !duplicates.isEmpty else { return nil }
141    
142    let allDuplicates = duplicates.keys
143      .sorted(by: { $0.name < $1.name })
144      .enumerated()
145      .map({ (index, variable) -> String in
146        (index > 0 && index == duplicates.count-1 ? "and " : "") + variable.name.singleQuoted
147      })
148      .joined(separator: duplicates.count > 2 ? ", " : " ")
149    
150    if duplicates.count > 1 {
151      return "\(mockableType.name.singleQuoted) contains the properties \(allDuplicates) that each conflict with inherited declarations and cannot be mocked"
152    } else {
153      return "\(mockableType.name.singleQuoted) contains the property \(allDuplicates) that conflicts with an inherited declaration and cannot be mocked"
154    }
155  }
156  
157  /// Classes that define designated initializers but none which are accessible.
158  var nonMockableDesignatedInitializerMessage: String? {
159    guard mockableType.kind == .class, !shouldGenerateDefaultInitializer else { return nil }
160    guard !mockableType.methods.contains(where: { $0.isDesignatedInitializer && $0.isMockable })
161      else { return nil }
162    return "\(mockableType.name.singleQuoted) does not declare any accessible designated initializers and cannot be mocked"
163  }
164  
165  /// Classes that cannot be initialized due to imported accessibility from an external module.
166  var nonMockableExternalInitializerMessage: String? {
167    guard mockableType.kind == .class, mockableType.subclassesExternalType else { return nil }
168    guard !mockableType.methods.contains(where: { $0.isInitializer && $0.isMockable })
169      else { return nil }
170    return "\(mockableType.name.singleQuoted) subclasses a type from a different module but does not declare any accessible initializers and cannot be mocked"
171  }
172  
173  lazy var unavailableMockAttribute: String = {
174    guard let message = nonMockableOpaqueInheritanceMessage
175      ?? nonMockableInheritedTypeMessage
176      ?? nonMockablePropertiesMessage
177      ?? nonMockableDesignatedInitializerMessage
178      ?? nonMockableExternalInitializerMessage
179      else { return "" }
180    
181    logWarning(
182      message,
183      diagnostic: .notMockable,
184      filePath: mockableType.filePath,
185      line: self.mockableType.lineNumber
186    )
187    
188    return """
189    @available(*, unavailable, message: "\(message)")
190    """
191  }()
192  
193  /// The static mocking context allows static or class methods to be mocked.
194  var staticMockingContext: String {
195    if !mockableType.genericTypes.isEmpty || mockableType.isInGenericContainingType {
196      // Class-level generic types don't support static variables directly.
197      let body = !shouldGenerateThunks ? Constants.thunkStub :
198        "return genericStaticMockContext.resolveTypeNames([\(runtimeGenericTypeNames)])"
199      return """
200      static var staticMock: Mockingbird.StaticMock \(BlockTemplate(body: body, multiline: false))
201      """
202    } else {
203      return "static let staticMock = Mockingbird.StaticMock()"
204    }
205  }
206  
207  lazy var runtimeGenericTypeNames: String = {
208    let baseTypeName = "\(mockableType.name)Mock\(allGenericTypes)"
209    let genericTypeSelfNames = mockableType.genericTypes
210      .sorted(by: { $0.name < $1.name })
211      .map({ "Swift.ObjectIdentifier(\($0.name).self).debugDescription" })
212    return String(list: [baseTypeName.doubleQuoted] + genericTypeSelfNames)
213  }()
214  
215  lazy var genericTypes: [String] = {
216    return mockableType.genericTypes.map({ $0.flattenedDeclaration })
217  }()
218  
219  lazy var allGenericTypes: String = {
220    guard !mockableType.genericTypes.isEmpty else { return "" }
221    return "<\(separated: mockableType.genericTypes.map({ $0.name }))>"
222  }()
223  
224  var allInheritedTypes: String {
225    return String(list: inheritedTypes + [Constants.mockProtocolName])
226  }
227  
228  /// For scoped types referenced within their containing type.
229  lazy var fullyQualifiedName: String = {
230    guard mockableType.kind == .class, mockableType.isContainedType else {
231      return mockableType.fullyQualifiedModuleName
232    }
233    return "\(mockableType.name)\(allGenericTypes)"
234  }()
235  
236  /// For scoped types referenced at the top level but in the same module.
237  func createScopedName(with containingTypeNames: [String],
238                        genericTypeContext: [[String]],
239                        suffix: String = "",
240                        moduleQualified: Bool = false) -> String {
241    let name = moduleQualified ?
242      mockableType.fullyQualifiedModuleName.removingGenericTyping() : mockableType.name
243    guard mockableType.kind == .class else { // Protocols can't be nested
244      return name + suffix + (!suffix.isEmpty ? allGenericTypes : "")
245    }
246    guard mockableType.isContainedType else {
247      return name + suffix + allGenericTypes
248    }
249    
250    let typeNames = containingTypeNames.enumerated()
251      .map({ (index, typeName) -> String in
252        guard let genericTypeNames = genericTypeContext.get(index), !genericTypeNames.isEmpty else {
253          return typeName + suffix
254        }
255        // Disambiguate generic types that shadow those defined by a containing type.
256        return typeName + suffix + "<\(separated: genericTypeNames.map({ typeName + "_" + $0 }))>"
257      })
258      + [mockableType.name + suffix + allGenericTypes]
259    
260    if moduleQualified && mockableType.fullyQualifiedName != mockableType.fullyQualifiedModuleName {
261      return String(list: [mockableType.moduleName] + typeNames, separator: ".")
262    } else {
263      return String(list: typeNames, separator: ".")
264    }
265  }
266  
267  lazy var protocolClassConformance: String? = {
268    guard mockableType.kind == .protocol,
269      let classConformance = mockableType.primarySelfConformanceTypeName
270      else { return nil }
271    
272    // Handle class conformance constraints from where clauses.
273    return classConformance
274  }()
275  
276  var inheritedTypes: [String] {
277    var types = [String]()
278    if let protocolClassConformance = self.protocolClassConformance {
279      types.append(protocolClassConformance)
280    }
281    types.append(fullyQualifiedName)
282    
283    let classConformanceTypeNames = Set(
284      mockableType.selfConformanceTypes
285        .filter({ $0.kind == .class })
286        .map({ $0.fullyQualifiedModuleName })
287    )
288    let conformanceTypes = Set(mockableType.allSelfConformanceTypeNames)
289      .subtracting(classConformanceTypeNames)
290      .subtracting(types)
291      .sorted()
292    return types + conformanceTypes
293  }
294  
295  func renderBody() -> String {
296    let supertypeDeclaration = "typealias MockingbirdSupertype = \(fullyQualifiedName)"
297    let mockingbirdContext = """
298    public let mockingbirdContext = Mockingbird.Context(["generator_version": "\(mockingbirdVersion.shortString)", "module_name": "\(mockableType.moduleName)"])
299    """
300    
301    var components = [String(lines: [
302      // Type declarations
303      supertypeDeclaration,
304      
305      // Properties
306      staticMockingContext,
307      mockingbirdContext,
308    ])]
309    
310    if isAvailable {
311      components.append(contentsOf: [
312        renderInitializerProxy(),
313        renderVariables(),
314        defaultInitializer,
315        renderMethods(),
316        renderContainedTypes()
317      ])
318    } else {
319      components.append(renderContainedTypes())
320    }
321    
322    return String(lines: components, spacing: 2)
323  }
324  
325  func renderContainedTypes() -> String {
326    guard !mockableType.containedTypes.isEmpty else { return "" }
327    let containedTypesSubstructure = mockableType.containedTypes
328      .map({
329        MockableTypeTemplate(mockableType: $0, mockedTypeNames: mockedTypeNames)
330          .render().indent()
331      })
332    return String(lines: containedTypesSubstructure, spacing: 2)
333  }
334  
335  func renderInitializerProxy() -> String {
336    let isProxyable: (Method) -> Bool = {
337      // This needs to be a designated initializer since if it's a convenience initializer, we can't
338      // always infer what concrete argument values to pass to the designated initializer.
339      $0.isDesignatedInitializer && $0.isMockable
340    }
341    
342    guard !shouldGenerateDefaultInitializer else { return "" }
343    let initializers = mockableType.methods
344      .filter(isProxyable)
345      .filter(isOverridable)
346      .sorted()
347      .compactMap({ methodTemplate(for: $0).classInitializerProxy })
348    
349    guard !initializers.isEmpty else {
350      return "public enum InitializerProxy {}"
351    }
352    return NominalTypeDefinitionTemplate(declaration: "public enum InitializerProxy",
353                                         body: String(lines: initializers, spacing: 2)).render()
354  }
355  
356  /// Store the source location of where the mock was initialized. This allows `XCTest` errors from
357  /// unstubbed method invocations to show up in the testing code.
358  var shouldGenerateDefaultInitializer: Bool {
359    // Opaque types can have designated initializers we don't know about, so it's best to ignore.
360    guard mockableType.opaqueInheritedTypeNames.isEmpty else {
361      logWarning(
362        "Unable to synthesize default initializer for \(mockableType.name.singleQuoted) which inherits from an external type not defined in a supporting source file",
363        diagnostic: .undefinedType,
364        filePath: mockableType.filePath,
365        line: self.mockableType.lineNumber
366      )
367      return false
368    }
369    
370    let hasDesignatedInitializer =
371      mockableType.methods.contains(where: { $0.isDesignatedInitializer })
372    
373    guard mockableType.kind == .protocol else { // Handle classes.
374      if hasDesignatedInitializer {
375        log("Skipping default initializer generation for \(mockableType.name.singleQuoted) because it is a class with a designated initializer")
376      }
377      return !hasDesignatedInitializer
378    }
379    
380    // We can always generate a default initializer for protocols without class conformance.
381    guard protocolClassConformance != nil else { return true }
382    
383    // Ignore protocols conforming to a class with a designated initializer.
384    guard !hasDesignatedInitializer else {
385      log("Skipping default initializer generation for \(mockableType.name.singleQuoted) because it is a protocol conforming to a class with a designated initializer")
386      return false
387    }
388    
389    let isMockableClassConformance = mockableType.primarySelfConformanceType?.shouldMock ?? true
390    if !isMockableClassConformance {
391      logWarning(
392        "\(mockableType.name.singleQuoted) conforms to a class without public initializers and cannot be initialized",
393        diagnostic: .notMockable,
394        filePath: mockableType.filePath,
395        line: self.mockableType.lineNumber
396      )
397    }
398    return isMockableClassConformance
399  }
400  
401  var defaultInitializer: String {
402    guard shouldGenerateDefaultInitializer else { return "" }
403    let canCallSuper = mockableType.kind == .class || protocolClassConformance != nil
404    return FunctionDefinitionTemplate(
405      declaration: "fileprivate init(sourceLocation: Mockingbird.SourceLocation)",
406      body: String(lines: [
407        canCallSuper ? "super.init()" : "",
408        "self.mockingbirdContext.sourceLocation = sourceLocation",
409        "\(mockableType.name)Mock.staticMock.mockingbirdContext.sourceLocation = sourceLocation",
410      ])).render()
411  }
412  
413  lazy var containsOverridableDesignatedInitializer: Bool = {
414    return mockableType.methods.contains(where: {
415      $0.isOverridable && $0.isDesignatedInitializer && $0.isMockable
416    })
417  }()
418  
419  func renderVariables() -> String {
420    return String(lines: mockableType.variables.sorted(by: <).map({
421      VariableTemplate(variable: $0, context: self).render()
422    }), spacing: 2)
423  }
424  
425  func isOverridable(method: Method) -> Bool {
426    let isClassMock = mockableType.kind == .class || mockableType.primarySelfConformanceType != nil
427    let isGeneric = !method.whereClauses.isEmpty || !method.genericTypes.isEmpty
428    guard isClassMock, isGeneric else { return true }
429    
430    // Not possible to override overloaded methods where uniqueness is from generic constraints.
431    // https://forums.swift.org/t/cannot-override-more-than-one-superclass-declaration/22213
432    // This is fixed in Swift 5.2, so non-overridable methods require compilation conditions.
433    return mockableType.methodsCount[Method.Reduced(from: method)] == 1
434  }
435  
436  func renderMethods() -> String {
437    return String(lines: Set(mockableType.methods).sorted(by: <).filter({ $0.isMockable }).map({
438      let renderedMethod = methodTemplate(for: $0).render()
439      guard !isOverridable(method: $0) else { return renderedMethod }
440      return String(lines: [
441        "#if swift(>=5.2)",
442        renderedMethod,
443        "#endif",
444      ])
445    }), spacing: 2)
446  }
447  
448  func specializeTypeName(_ typeName: String) -> String {
449    // NOTE: Checking for an indicator prior to running `replacingOccurrences` is 4x faster.
450    let concreteMockTypeName = mockableType.name + "Mock"
451    
452    if typeName.contains(SerializationRequest.Constants.selfTokenIndicator) {
453      return typeName.replacingOccurrences(of: SerializationRequest.Constants.selfToken,
454                                           with: concreteMockTypeName)
455    }
456    
457    if typeName.contains(SerializationRequest.Constants.syntheticSelfTokenIndicator) {
458      return typeName.replacingOccurrences(of: SerializationRequest.Constants.syntheticSelfToken,
459                                           with: concreteMockTypeName)
460    }
461    
462    return typeName
463  }
464}
465
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Most used methods in MethodTemplate

Run Selenium Automation Tests on LambdaTest Cloud Grid

Trigger Selenium automation tests on a cloud-based Grid of 3000+ real browsers and operating systems.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)