Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .sourcery.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
sources:
- ./Example/SwiftyMock/RoboKitten
# this one is needed for templates to recognize ReactiveSwift types if we want to stub them
- ./Example/Pods/ReactiveSwift/Sources
templates:
- ./SwiftyMock/Templates
output:
path: ./Example/RoboKittenTests/Mocks/Generated
args:
testable: SwiftyMock_Example
testable: SwiftyMock_Example
# hard-coding these for now until it's clear how to dynamicall get modules for used types, more here: https://github.com/krzysztofzablocki/Sourcery/issues/670
imports:
- ReactiveSwift
- Result
34 changes: 24 additions & 10 deletions Example/RoboKittenTests/Mocks/Generated/Mock.generated.swift
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
// Generated using Sourcery 0.13.1 — https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 0.14.0 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT

import Foundation
import SwiftyMock
import ReactiveSwift
import Result
@testable import SwiftyMock_Example

class FakeLazyRoboKitten: LazyRoboKitten {
let needsRestGetCall = FunctionVoidCall<Bool>()
let needsRestSetCall = FunctionCall<Bool, Void>()
var needsRest: Bool {
get { return stubCall(needsRestGetCall) }
set { stubCall(needsRestSetCall, argument: newValue) }
var needsRest: Bool

let wantsToEatGetCall = FunctionCall<Void, Bool>()
let wantsToEatSetCall = FunctionCall<Bool, Void>()
var wantsToEat: Bool {
get { return stubCall(wantsToEatGetCall, argument: ()) }
set { stubCall(wantsToEatSetCall, argument: newValue) }
}

init(needsRest: Bool) {
self.needsRest = needsRest
}

let batteryStatusCall = FunctionVoidCall<Int>()
let sleepCall = ReactiveCall<Int, Bool, NSError>()
func sleep(hours: Int) -> SignalProducer<Bool, NSError> {
return stubCall(sleepCall, argument: hours)
}

let batteryStatusCall = FunctionCall<(), Int>()
func batteryStatus() -> Int {
return stubCall(batteryStatusCall)
return stubCall(batteryStatusCall, argument: ())
}

let jumpCall = FunctionCall<(x: Int, y: Int), Void>()
Expand All @@ -36,9 +49,10 @@ class FakeLazyRoboKitten: LazyRoboKitten {

class FakeRoboKitten: RoboKitten {

let batteryStatusCall = FunctionVoidCall<Int>()

let batteryStatusCall = FunctionCall<(), Int>()
func batteryStatus() -> Int {
return stubCall(batteryStatusCall)
return stubCall(batteryStatusCall, argument: ())
}

let jumpCall = FunctionCall<(x: Int, y: Int), Void>()
Expand Down
7 changes: 6 additions & 1 deletion Example/SwiftyMock/RoboKitten/RoboKitten.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//

import Foundation
import ReactiveSwift

// sourcery: Mock
protocol RoboKitten {
Expand All @@ -15,5 +16,9 @@ protocol RoboKitten {

// sourcery: Mock
protocol LazyRoboKitten: RoboKitten {
// sourcery: skipMock
var needsRest: Bool { get set }
}
var wantsToEat: Bool { get set }

func sleep(hours: Int) -> SignalProducer<Bool, NSError>
}
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,13 @@ protocol RoboKitten {
```
Third, run sourcery command `sourcery --config .sourcery.yml --watch` if you want to run service that will regenerate mocks every time your source files or templates change.
Or `sourcery --config .sourcery.yml` if you want to generate mocks once.

### Some Extras
We're able to generate templates for stubbbing and mocking `SignalProducer` from ReactiveSwift.
But, to make it work correctly, you should add path to its sources in sourcery config.

There's one more step to have generated file compile.
Currently, we can't get all modules' names, whose types are participating in generated file.
There's [issue](https://github.com/krzysztofzablocki/Sourcery/issues/670) in Sourcery repo, that might elaborate on this task.
So, to make file compile, we need to manually (for now) import modules. Since file is always regenerated, we can do it through sourcery config file.
List all imports inside `imports` argument, as it's done here in the example `.sourcery.yml`.
38 changes: 28 additions & 10 deletions SwiftyMock/Templates/Mock.stencil
Original file line number Diff line number Diff line change
@@ -1,40 +1,58 @@
import Foundation
import SwiftyMock
{# So far getting imports hardcoded from config, correct approach might be found here: https://github.com/krzysztofzablocki/Sourcery/issues/670 #}
{% for import in argument.imports %}
import {{ import }}
{% endfor %}

{% if argument.testable %}@testable import {{ argument.testable }}{% endif %}

{% macro functionCallType method %}{% if method.parameters.count == 0 %}FunctionVoidCall{% else %}FunctionCall{% endif %}{% endmacro %}

{% macro functionCallArgumentsType method %}{% if method.parameters.count == 1 %}{{ method.parameters.first.typeName.unwrappedTypeName }}{% else %}({% for param in method.parameters %}{{ param.name }}: {{ param.typeName.unwrappedTypeName }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %}{% endmacro %}

{% macro functionCallReturnType method %}{% if not method.returnTypeName.isVoid %}{{ method.returnTypeName }}{% else %}Void{% endif %}{% endmacro %}
{% macro functionCall method %}FunctionCall<{% call functionCallArgumentsType method %}, {% if not method.returnTypeName.isVoid %}{{ method.returnTypeName }}{% else %}Void{% endif %}>{% endmacro %}
{% macro reactiveCall method %}ReactiveCall<{% call functionCallArgumentsType method %}, {% for typeParameter in method.returnTypeName.generic.typeParameters %}{{ typeParameter.typeName }}{% if not forloop.last %}, {% endif %}{% endfor %}>{% endmacro %}

{% macro stubCallArguments method %}{% if method.parameters.count > 0 %}, argument: {% if method.parameters.count == 1 %}{{ method.parameters.first.name }}{% else %}({% for param in method.parameters %}{{ param.name }}: {{ param.name }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %}{% endif %}{% endmacro %}
{% macro functionCallType method %}{% if method.returnType.name == "SignalProducer" %}{% call reactiveCall method %}{% else %}{% call functionCall method %}{% endif %}{% endmacro %}

{% macro mockVariableGetterCall variable %}let {{ variable.name }}GetCall = FunctionVoidCall<{{ variable.typeName }}>(){% endmacro %}
{% macro stubCallArguments method %}, argument: {% if method.parameters.count == 0 %}(){% elif method.parameters.count == 1 %}{{ method.parameters.first.name }}{% else %}({% for param in method.parameters %}{{ param.name }}: {{ param.name }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %}{% endmacro %}

{% macro mockVariableGetterCall variable %}let {{ variable.name }}GetCall = FunctionCall<Void, {{ variable.typeName }}>(){% endmacro %}
{% macro mockVariableSetterCall variable %}let {{ variable.name }}SetCall = FunctionCall<{{ variable.typeName }}, Void>(){% endmacro %}

{% macro mockVariable variable %}
{% call mockVariableGetterCall variable %}
{% if variable.isMutable %}{% call mockVariableSetterCall variable %}{% endif %}
var {{ variable.name }}: {{ variable.typeName }} {
get { return stubCall({{ variable.name }}GetCall) }
get { return stubCall({{ variable.name }}GetCall, argument: ()) }
{% if variable.isMutable %}set { stubCall({{ variable.name }}SetCall, argument: newValue) }{% endif %}
}
{% endmacro %}

{% macro property variable %}
{% if variable.isMutable %}var{% else %}let{% endif %} {{ variable.name }}: {{ variable.typeName }}
{% endmacro %}

{% for type in types.protocols where type|annotated:"Mock" %}
class Fake{{ type.name }}: {{ type.name }} {
{% for variable in type.allVariables|!definedInExtension %}
{% call mockVariable variable %}
{% if variable|!annotated:"skipMock" %}{% call mockVariable variable %}{% else %}{% call property variable %}{% endif %}
{% if not forloop.last %}

{% endif %}
{% endfor %}

{% for method in type.allMethods|!definedInExtension %}
let {{ method.shortName }}Call = {% call functionCallType method %}<{% if method.parameters.count > 0 %}{% call functionCallArgumentsType method %}, {% endif %}{% call functionCallReturnType method %}>()
{% if type.allVariables|!definedInExtension|annotated:"skipMock" %}
init({% for variable in type.allVariables|!definedInExtension|annotated:"skipMock" %}{{ variable.name }}: {{ variable.typeName }}{% if not forloop.last %}, {% endif %}{% endfor %}) {
{% for variable in type.allVariables|!definedInExtension|annotated:"skipMock" %}
self.{{ variable.name }} = {{ variable.name }}
{% endfor %}
}
{% endif %}

{% for method in type.allMethods|!definedInExtension where method|!annotated:"skipMock" %}
let {{ method.shortName }}Call = {% call functionCallType method %}()
func {{ method.name }}{% if method.throws %} throws{% endif %}{% if not method.returnTypeName.isVoid %} -> {{ method.returnTypeName }}{% endif %} {
return stubCall({{ method.shortName }}Call{% call stubCallArguments method %}{% if method.returnTypeName.isVoid %}, defaultValue: ()){% else %}){% endif %}
return stubCall({{ method.shortName }}Call{% call stubCallArguments method %}{% if method.returnTypeName.isVoid %}, defaultValue: (){% endif %})
}
{% if not forloop.last %}

Expand Down