Created
August 4, 2020 07:55
-
-
Save alexito4/75a085b2cb54a7969c036efa65e74c3f to your computer and use it in GitHub Desktop.
Sourcery templates for automatic mocks from a concrete type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// swiftlint:disable line_length | |
// swiftlint:disable variable_name | |
import Foundation | |
#if os(iOS) || os(tvOS) || os(watchOS) | |
import UIKit | |
#elseif os(OSX) | |
import AppKit | |
#endif | |
import RxSwift | |
@testable import WorkAngel | |
{% macro swiftifyMethodName name %}{{ name | replace:"(","_" | replace:")","" | replace:":","_" | replace:"`","" | snakeToCamelCase | lowerFirstWord }}{% endmacro %} | |
{% macro methodThrowableErrorDeclaration method %} | |
var {% call swiftifyMethodName method.selectorName %}ThrowableError: Error? | |
{% endmacro %} | |
{% macro methodThrowableErrorUsage method %} | |
if let error = {% call swiftifyMethodName method.selectorName %}ThrowableError { | |
throw error | |
} | |
{% endmacro %} | |
{% macro methodReceivedParameters method %} | |
{%if method.parameters.count == 1 %} | |
{% call swiftifyMethodName method.selectorName %}Received{% for param in method.parameters %}{{ param.name|upperFirstLetter }} = {{ param.name }}{% endfor %} | |
{% call swiftifyMethodName method.selectorName %}ReceivedInvocations.append({% for param in method.parameters %}{{ param.name }}){% endfor %} | |
{% else %} | |
{% if not method.parameters.count == 0 %} | |
{% call swiftifyMethodName method.selectorName %}ReceivedArguments = ({% for param in method.parameters %}{{ param.name }}: {{ param.name }}{% if not forloop.last%}, {% endif %}{% endfor %}) | |
{% call swiftifyMethodName method.selectorName %}ReceivedInvocations.append(({% for param in method.parameters %}{{ param.name }}: {{ param.name }}{% if not forloop.last%}, {% endif %}{% endfor %})) | |
{% endif %} | |
{% endif %} | |
{% endmacro %} | |
{% macro methodClosureName method %}{% call swiftifyMethodName method.selectorName %}Closure{% endmacro %} | |
{% macro closureReturnTypeName method %}{% if method.isOptionalReturnType %}{{ method.unwrappedReturnTypeName }}?{% else %}{{ method.returnTypeName }}{% endif %}{% endmacro %} | |
{% macro methodClosureDeclaration method %} | |
var {% call methodClosureName method %}: (({% for param in method.parameters %}{{ param.typeName }}{% if not forloop.last %}, {% endif %}{% endfor %}) {% if method.throws %}throws {% endif %}-> {% if method.isInitializer %}Void{% else %}{% call closureReturnTypeName method %}{% endif %})? | |
{% endmacro %} | |
{% macro methodClosureCallParameters method %}{% for param in method.parameters %}{{ param.name }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endmacro %} | |
{% macro mockMethod method %} | |
//MARK: - {{ method.shortName }} | |
{% if method.throws %} | |
{% call methodThrowableErrorDeclaration method %} | |
{% endif %} | |
{% if not method.isInitializer %} | |
var {% call swiftifyMethodName method.selectorName %}CallsCount = 0 | |
var {% call swiftifyMethodName method.selectorName %}Called: Bool { | |
return {% call swiftifyMethodName method.selectorName %}CallsCount > 0 | |
} | |
{% endif %} | |
{% if method.parameters.count == 1 %} | |
var {% call swiftifyMethodName method.selectorName %}Received{% for param in method.parameters %}{{ param.name|upperFirstLetter }}: {{ '(' if param.isClosure }}{{ param.typeName.unwrappedTypeName }}{{ ')' if param.isClosure }}?{% endfor %} | |
var {% call swiftifyMethodName method.selectorName %}ReceivedInvocations{% for param in method.parameters %}: [{{ '(' if param.isClosure }}{{ param.typeName.unwrappedTypeName }}{{ ')' if param.isClosure }}{%if param.typeName.isOptional%}?{%endif%}]{% endfor %} = [] | |
{% elif not method.parameters.count == 0 %} | |
var {% call swiftifyMethodName method.selectorName %}ReceivedArguments: ({% for param in method.parameters %}{{ param.name }}: {{ param.unwrappedTypeName if param.typeAttributes.escaping else param.typeName }}{{ ', ' if not forloop.last }}{% endfor %})? | |
var {% call swiftifyMethodName method.selectorName %}ReceivedInvocations: [({% for param in method.parameters %}{{ param.name }}: {{ param.unwrappedTypeName if param.typeAttributes.escaping else param.typeName }}{{ ', ' if not forloop.last }}{% endfor %})] = [] | |
{% endif %} | |
{% if not method.returnTypeName.isVoid and not method.isInitializer %} | |
var {% call swiftifyMethodName method.selectorName %}ReturnValue: {{ '(' if method.returnTypeName.isClosure and not method.isOptionalReturnType }}{{ method.returnTypeName }}{{ ')' if method.returnTypeName.isClosure and not method.isOptionalReturnType }}{{ '!' if not method.isOptionalReturnType }} | |
{% endif %} | |
{% call methodClosureDeclaration method %} | |
{% if method.isInitializer %} | |
required {{ method.name }} { | |
{% call methodReceivedParameters method %} | |
{% call methodClosureName method %}?({% call methodClosureCallParameters method %}) | |
} | |
{% else %} | |
func {{ method.name }}{{ ' throws' if method.throws }}{% if not method.returnTypeName.isVoid %} -> {{ method.returnTypeName }}{% endif %} { | |
{% if method.throws %} | |
{% call methodThrowableErrorUsage method %} | |
{% endif %} | |
{% call swiftifyMethodName method.selectorName %}CallsCount += 1 | |
{% call methodReceivedParameters method %} | |
{% if method.returnTypeName.isVoid %} | |
{% if method.throws %}try {% endif %}{% call methodClosureName method %}?({% call methodClosureCallParameters method %}) | |
{% else %} | |
return {{ 'try ' if method.throws }}{% call methodClosureName method %}.map({ {{ 'try ' if method.throws }}$0({% call methodClosureCallParameters method %}) }) ?? {% call swiftifyMethodName method.selectorName %}ReturnValue | |
{% endif %} | |
} | |
{% endif %} | |
{% endmacro %} | |
{% macro mockOptionalVariable variable %} | |
var {% call mockedVariableName variable %}: {{ variable.typeName }} | |
{% endmacro %} | |
{% macro mockNonOptionalArrayOrDictionaryVariable variable %} | |
var {% call mockedVariableName variable %}: {{ variable.typeName }} = {% if variable.isArray %}[]{% elif variable.isDictionary %}[:]{% endif %} | |
{% endmacro %} | |
{% macro mockNonOptionalVariable variable %} | |
var {% call mockedVariableName variable %}: {{ variable.typeName }} { | |
get { return {% call underlyingMockedVariableName variable %} } | |
set(value) { {% call underlyingMockedVariableName variable %} = value } | |
} | |
var {% call underlyingMockedVariableName variable %}: {{ variable.typeName }}! | |
{% endmacro %} | |
{% macro underlyingMockedVariableName variable %}underlying{{ variable.name|upperFirstLetter }}{% endmacro %} | |
{% macro mockedVariableName variable %}{{ variable.name }}{% endmacro %} | |
{% for type in types.protocols where type.based.AutoMockable or type|annotated:"AutoMockable" %}{% if type.name != "AutoMockable" %} | |
class {{ type.name }}Mock: {{ type.name }} { | |
{% for variable in type.allVariables|!definedInExtension %} | |
{% if variable.isOptional %}{% call mockOptionalVariable variable %}{% elif variable.isArray or variable.isDictionary %}{% call mockNonOptionalArrayOrDictionaryVariable variable %}{% else %}{% call mockNonOptionalVariable variable %}{% endif %} | |
{% endfor %} | |
{% for method in type.allMethods|!definedInExtension %} | |
{% call mockMethod method %} | |
{% endfor %} | |
} | |
{% endif %}{% endfor %} | |
{# Support AutoProtocol and AutoMockable combo. Logic for vars and methods is repeated from AutoProtocol. Is done like this because sourcery won't generate the automock for something that has been genrated by autoprotol. We could add automockable and autoprotocol together but we want them in different files/targets. #} | |
{% for type in types.all where type.kind != "protocol" and type|annotated:"AutoMockable" and type|annotated:"AutoProtocol" %} | |
// AutoMockable + AutoProtocol | |
class {{ type.name }}Mock: {{ type.name }}Protocol { | |
{% for p in type.variables|!definedInExtension where p.readAccess != "private" and p.readAccess != "fileprivate" %} | |
{% if p.isOptional %}{% call mockOptionalVariable p %}{% elif p.isArray or p.isDictionary %}{% call mockNonOptionalArrayOrDictionaryVariable p %}{% else %}{% call mockNonOptionalVariable p %}{% endif %} | |
{% endfor %} | |
{% for m in type.methods|!definedInExtension where m.readAccess != "private" and m.readAccess != "fileprivate" and m.callName != "init" %} | |
{% call mockMethod m %} | |
{% endfor %} | |
} | |
{% endfor %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import RxSwift | |
{% for type in types.all where type|annotated:"AutoProtocol" %} | |
{% set protoName %}{{ type.localName }}Protocol{% endset %} | |
// MARK: {{ protoName }} | |
{{ type.attributes.objc }} protocol {{ protoName }} { | |
{% for p in type.variables|!definedInExtension where p.readAccess != "private" and p.readAccess != "fileprivate" %} | |
var {{ p.name }}: {{ p.actualTypeName }} { get{% if p.isMutable %} set{% endif %} } | |
{% endfor %} | |
{% for m in type.methods|!definedInExtension where m.accessLevel != "private" and m.accessLevel != "fileprivate" and m.callName != "init" %} | |
func {{ m.name }} -> {{ m.actualReturnTypeName }} | |
{% endfor %} | |
} | |
extension {{ type.name }}: {{ protoName }} {} | |
{% endfor %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// sourcery: AutoProtocol, AutoMockable | |
final class ConcreteType { | |
... | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment