Skip to content

Instantly share code, notes, and snippets.

@marinbenc
Last active February 3, 2018 23:35
Show Gist options
  • Save marinbenc/a4d377bec0765b24780ebaa8b568c3c5 to your computer and use it in GitHub Desktop.
Save marinbenc/a4d377bec0765b24780ebaa8b568c3c5 to your computer and use it in GitHub Desktop.
Automatically create simple mocks for protocols in Swift
{% for type in types.protocols.implementing.AutoMockable %}
{% if not type.name == "AutoMockable" %}
class {{ type.name }}Mock: {{ type.name }} {
{% for method in type.allMethods %}
//MARK: - {{ method.shortName }}
var {{ method.shortName }}Called = false
{% if not method.parameters.count == 0 %}var {{ method.shortName }}RecievedArguments: ({% for param in method.parameters %}{{ param.name }}: {{ param.typeName }}{% if not forloop.last %}, {% endif %}{% endfor %})?
{% endif %}{% if not method.returnTypeName.isVoid %}
var {{ method.shortName }}ReturnValue: {{ method.returnTypeName }}!
{% endif %}
func {{ method.shortName }}({% for param in method.parameters %}{{ param.argumentLabel }}{% if not param.argumentLabel == param.name %} {{ param.name }}{% endif %}: {{ param.typeName }}{% if not forloop.last %}, {% endif %}{% endfor %}){% if not method.returnTypeName.isVoid %} -> {{ method.returnTypeName }}{% endif %} {
{{ method.shortName }}Called = true
{{ method.shortName }}RecievedArguments = ({% for param in method.parameters %}{{ param.name }}: {{ param.name }}{% if not forloop.last%}, {% endif %}{% endfor %}){% if not method.returnTypeName.isVoid %}
return {{ method.shortName }}ReturnValue{% endif %}
}
{% endfor %}
}
{% endif %}
{% endfor %}
import Foundation
/// Protocols confoming to this protocol will have a mock generated for them.
protocol AutoMockable {}
protocol DataServiceProtocol: AutoMockable {
func fullUrl(forPath path: String)-> String
func fetchResource(fromURL url: URL, onComplete: (Result)-> Void)
}
class DataService: DataServiceProtocol {
func fullUrl(forPath path: String)-> String {
//Real implementation
//Will get replaced in the generated file
return baseURL + path
}
func fetchResource(fromURL url: URL, onComplete: (Result)-> Void) {
//Real implementation
//Will get replaced in the generated file
}
}
// Generated using Sourcery 0.5.0 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
class DataServiceProtocolMock: DataServiceProtocol {
//MARK: - fullUrl
var fullUrlCalled = false
var fullUrlRecievedArguments: (path: String)?
var fullUrlReturnValue: String!
func fullUrl(forPath path: String) -> String {
fullUrlCalled = true
fullUrlRecievedArguments = (path: path)
return fullUrlReturnValue
}
//MARK: - fetchResource
var fetchResourceCalled = false
var fetchResourceRecievedArguments: (url: URL, onComplete: (Result)-> Void)?
func fetchResource(fromURL url: URL, onComplete: (Result)-> Void) {
fetchResourceCalled = true
fetchResourceRecievedArguments = (url: url, onComplete: onComplete)
}
}

Automatically generates a mock class implementing a specific protocol. For each protocol method, the mock will provide a methodNameCalled boolean. Files:

  • DataService.swift - sample file
  • lens.stencil - sourcery template
  • DataServiceMock.swift - sample mock generated

You will need sourcery template for this to work.

Issues and limitations:

  • Function parameters that are @escaping closures will produce a compiler error when declaring the recievedArguments tuple, because functions tuples are implicitly escaping, so there's no need for @escaping. To fix this, simply remove the @escaping from the declaration.
  • For now, only protocol methods will be mocked. Variables are coming!
  • Overloaded methods will produce compiler warnings since they have the same name.

Suggestions and improvements are welcome!

Inspired by Filip Zawada.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment