Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto mock template with opaque types #1149

Closed
utahwithak opened this issue Mar 2, 2023 · 1 comment
Closed

Auto mock template with opaque types #1149

utahwithak opened this issue Mar 2, 2023 · 1 comment

Comments

@utahwithak
Copy link

Running into an issue with opaque types and I'm wondering how best to adjust the stencil to properly add parens around it.

For example a protocol that has something like so:

protocol Foo {
   var item: (any Bar)? { get  set }

}

I'm seeing it output something like:

var item: any Bar?

It should output the parens around it when the type is opaque.

var item: (any Bar)?

Generating with 2.0.1

@paul1893
Copy link
Contributor

Hello 👋. It's opened since quite a while but in case it could help someone in the futur this stencil 👇 can help.

AutoMockable.stencil

// swiftlint:disable line_length
// swiftlint:disable variable_name
// swiftlint:disable weak_delegate

import Foundation
#if os(iOS) || os(tvOS) || os(watchOS)
import UIKit
#elseif os(OSX)
import AppKit
#endif

{% for import in argument.autoMockableImports %}
import {{ import }}
{% endfor %}

{% for import in argument.autoMockableTestableImports %}
@testable import {{ import }}
{% endfor %}

{% 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 %}{% if param.typeName|contains:"any" and param.typeName|contains:"?" %}{{ param.typeName | replace:"any","(any" | replace:"?",")?" }}{%else%}{{ param.typeName }}{%endif%}{% if not forloop.last %}, {% endif %}{% endfor %}) {% if method.isAsync %}async {% endif %}{% 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.typeName.isOptional%}({%endif%}{{ '(' 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 %}
    {% for name, attribute in method.attributes %}
    {% for value in attribute %}
    {{ value }}
    {% endfor %}
    {% endfor %}
    func {% if method.name|contains:"any" and method.name|contains:"?" %}{{ method.name | replace:"any","(any" | replace:"?",")?" }}{%else%}{{ method.name }}{%endif%}{{ ' async' if method.isAsync }}{{ ' 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 %}{% if method.isAsync %}await {% endif %}{% call methodClosureName method %}?({% call methodClosureCallParameters method %})
        {% else %}
        if let {% call methodClosureName method %} = {% call methodClosureName method %} {
            return {{ 'try ' if method.throws }}{{ 'await ' if method.isAsync }}{% call methodClosureName method %}({% call methodClosureCallParameters method %})
        } else {
            return {% call swiftifyMethodName method.selectorName %}ReturnValue
        }
        {% endif %}
    }

{% endif %}
{% endmacro %}

{% macro mockOptionalVariable variable %}
    var {% call mockedVariableName variable %}: {% if variable.typeName|contains:"any" and variable.typeName|contains:"?" %}{{ variable.typeName | replace:"any","(any" | replace:"?",")?" }}{% elif variable.typeName|contains:"any" and variable.typeName|contains:"!" %}{{ variable.typeName | replace:"any","(any" | replace:"!",")!" }}{%else%}{{ variable.typeName }}{%endif%}
{% 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 }
    }
    private var {% call underlyingMockedVariableName variable %}: {% if variable.typeName|contains:"any" %}{{ variable.typeName | replace:"any","(any" }})!{%else%}{{ variable.typeName }}!{%endif%}
{% 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" %}
final 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 %}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants