Giter Club home page Giter Club logo

mockolo's Introduction

CII Best Practices Build Status License FOSSA Status

Welcome to Mockolo

Mockolo is an efficient mock generator for Swift. Swift doesn't provide mocking support, and Mockolo provides a fast and easy way to autogenerate mock objects that can be tested in your code. One of the main objectives of Mockolo is fast performance. Unlike other frameworks, Mockolo provides highly performant and scalable generation of mocks via a lightweight commandline tool, so it can run as part of a linter or a build if one chooses to do so. Try Mockolo and enhance your project's test coverage in an effective, performant way.

Motivation

One of the main objectives of this project is high performance. There aren't many 3rd party tools that perform fast on a large codebase containing, for example, over 2M LoC or over 10K protocols. They take several hours and even with caching enabled take several minutes. Mockolo was built to make highly performant generation of mocks possible (in the magnitude of seconds) on such large codebase. It uses a minimal set of frameworks necessary (mentioned in the Used libraries section) to keep the code lean and efficient.

Another objective is to enable flexibility in using or overriding types if needed. This allows use of some of the features that require deeper analysis such as protocols with associated types to be simpler, more straightforward, and less fragile.

Disclaimer

This project may contain unstable APIs which may not be ready for general use. Support and/or new releases may be limited.

System Requirements

  • Swift 5.7 or later
  • Xcode 14.2 or later
  • macOS 12.0 or later and Linux
  • Support is included for the Swift Package Manager

Build / Install

Option 1: By Mint

$ mint install uber/mockolo
$ mint run uber/mockolo mockolo -h // see commandline input options below

Option 2: Homebrew

$ brew install mockolo

Option 3: Use the binary

Go to the Release tab and download/install the binary directly.

Option 4: Clone and build/run

$ git clone https://github.com/uber/mockolo.git
$ cd mockolo
$ swift build -c release
$ .build/release/mockolo -h  // see commandline input options below

To call mockolo from any location, copy the executable into a directory that is part of your PATH environment variable.

To check out a specific version,

$ git tag -l
$ git checkout [tag]

To use Xcode to build and run,

$ swift package generate-xcodeproj

Run

Mockolo is a commandline executable. To run it, pass in a list of the source file directories or file paths of a build target, and the destination filepath for the mock output. To see other arguments to the commandline, run mockolo --help.

./mockolo -s myDir -d ./OutputMocks.swift -x Images Strings

This parses all the source files in myDir directory, excluding any files ending with Images or Strings in the file name (e.g. MyImages.swift), and generates mocks to a file at OutputMocks.swift in the current directory.

Use --help to see the complete argument options.

./mockolo -h  // or --help

OVERVIEW: Mockolo: Swift mock generator.

USAGE: mockolo [<options>] --destination <destination>

OPTIONS:
  --allow-set-call-count  If set, generated *CallCount vars will be allowed to set manually.
  --annotation <annotation>
                          A custom annotation string used to indicate if a type should be mocked (default = @mockable). (default: @mockable)
  -j, --concurrency-limit <n>
                          Maximum number of threads to execute concurrently (default = number of cores on the running machine).
  --custom-imports <custom-imports>
                          If set, custom module imports (separated by a space) will be added to the final import statement list.
  --enable-args-history   Whether to enable args history for all functions (default = false). To enable history per function, use the 'history' keyword in the annotation argument.
  --disable-combine-default-values
                          Whether to disable generating Combine streams in mocks (default = false). Set this to true to control how your streams are created in your mocks.
  --exclude-imports <exclude-imports>
                          If set, listed modules (separated by a space) will be excluded from the import statements in the mock output.
  -x, --exclude-suffixes <exclude-suffixes>
                          List of filename suffix(es) without the file extensions to exclude from parsing (separated by a space).
  --header <header>       A custom header documentation to be added to the beginning of a generated mock file.
  -l, --logging-level <n> The logging level to use. Default is set to 0 (info only). Set 1 for verbose, 2 for warning, and 3 for error. (default: 0)
  --macro <macro>         If set, #if [macro] / #endif will be added to the generated mock file content to guard compilation.
  --mock-all              If set, it will mock all types (protocols and classes) with a mock annotation (default is set to false and only mocks protocols with a mock annotation).
  --mock-filelist <mock-filelist>
                          Path to a file containing a list of dependent files (separated by a new line) of modules this target depends on.
  --mock-final            If set, generated mock classes will have the 'final' attributes (default is set to false).
  -mocks, --mockfiles <mocks>
                          List of mock files (separated by a space) from modules this target depends on. If the --mock-filelist value exists, this will be ignored.
  -d, --destination <destination>
                          Output file path containing the generated Swift mock classes. If no value is given, the program will exit.
  -s, --sourcedirs <sourcedirs>
                          Paths to the directories containing source files to generate mocks for (separated by a space). If the --filelist or --sourcefiles values exist, they will be ignored.
  -f, --filelist <filelist>
                          Path to a file containing a list of source file paths (delimited by a new line). If the --sourcedirs value exists, this will be ignored.
  -srcs, --sourcefiles <srcs>
                          List of source files (separated by a space) to generate mocks for. If the --sourcedirs or --filelist value exists, this will be ignored.
  -i, --testable-imports <testable-imports>
                          If set, @testable import statements will be added for each module name in this list (separated by a space).
  --use-mock-observable   If set, a property wrapper will be used to mock RxSwift Observable variables (default is set to false).
  --use-template-func     If set, a common template function will be called from all functions in mock classes (default is set to false).
  -h, --help              Show help information.

Distribution

The install-script.sh will build and package up the mockolo binary and other necessary resources in the same bundle.

$ ./install-script.sh -h  // see input options
$ ./install-script.sh -s [source dir] -t mockolo -d [destination dir] -o [output filename].tar.gz

This will create a tarball for distribution, which contains the mockolo executable.

How to use

For example, Foo.swift contains:

/// @mockable
public protocol Foo {
    var num: Int { get set }
    func bar(arg: Float) -> String
}

Running ./mockolo -srcs Foo.swift -d ./OutputMocks.swift will output:

public class FooMock: Foo {
    init() {}
    init(num: Int = 0) {
        self.num = num
    }

    var numSetCallCount = 0
    var underlyingNum: Int = 0
    var num: Int {
        get {
            return underlyingNum
        }
        set {
            underlyingNum = newValue
            numSetCallCount += 1
        }
    }

    var barCallCount = 0
    var barHandler: ((Float) -> (String))?
    func bar(arg: Float) -> String {
        barCallCount += 1
        if let barHandler = barHandler {
            return barHandler(arg)
        }
        return ""
    }
}

The above mock can now be used in a test as follows:

func testMock() {
    let mock = FooMock(num: 5)
    XCTAssertEqual(mock.numSetCallCount, 1)
    mock.barHandler = { arg in
        return String(arg)
    }
    XCTAssertEqual(mock.barCallCount, 1)
}

Arguments

A list of override arguments can be passed in (delimited by a semicolon) to the annotation to set var types, typealiases, module names, etc.

Module

/// @mockable(module: prefix = Bar)
public protocol Foo {
    ...
}

This will generate:

public class FooMock: Bar.Foo {
    ...
}

Typealias

/// @mockable(typealias: T = AnyObject; U = StringProtocol)
public protocol Foo {
    associatedtype T
    associatedtype U: Collection where U.Element == T
    associatedtype W
    ...
}

This will generate the following mock output:

public class FooMock: Foo {
    typealias T = AnyObject // overridden
    typealias U = StringProtocol // overridden
    typealias W = Any // default placeholder type for typealias
    ...
}

RxSwift

For a var type such as an RxSwift observable:

/// @mockable(rx: intStream = ReplaySubject; doubleStream = BehaviorSubject)
public protocol Foo {
    var intStream: Observable<Int> { get }
    var doubleStream: Observable<Double> { get }
}

This will generate:

public class FooMock: Foo {
    var intStreamSubject = ReplaySubject<Int>.create(bufferSize: 1)
    var intStream: Observable<Int> { /* use intStreamSubject */ }
    var doubleStreamSubject = BehaviorSubject<Int>(value: 0)
    var doubleStream: Observable<Int> { /* use doubleStreamSubject */ }
}

Function Argument History

To capture function arguments history:

/// @mockable(history: fooFunc = true)
public protocol Foo {
    func fooFunc(val: Int)
    func barFunc(_ val: (a: String, Float))
    func bazFunc(val1: Int, val2: String)
}

This will generate:

public class FooMock: Foo {
    var fooFuncCallCount = 0
    var fooFuncArgValues = [Int]() // arguments captor
    var fooFuncHandler: ((Int) -> ())?
    func fooFunc(val: Int) {
        fooFuncCallCount += 1
        fooFuncArgValues.append(val)   // capture arguments

        if fooFuncHandler = fooFuncHandler {
            fooFuncHandler(val)
        }
    }

    ...
    var barFuncArgValues = [(a: String, Float)]() // tuple is also supported.
    ...

    ...
    var bazFuncArgValues = [(Int, String)]()
    ...
}

and also, enable the arguments captor for all functions if you passed --enable-args-history arg to mockolo command.

NOTE: The arguments captor only supports singular types (e.g. variable, tuple). The closure variable is not supported.

Combine's AnyPublisher

To generate mocks for Combine's AnyPublisher:

/// @mockable(combine: fooPublisher = PassthroughSubject; barPublisher = CurrentValueSubject)
public protocol Foo {
    var fooPublisher: AnyPublisher<String, Never> { get }
    var barPublisher: AnyPublisher<Int, CustomError> { get }
}

This will generate:

public class FooMock: Foo {
    public init() { }

    public var fooPublisher: AnyPublisher<String, Never> { return self.fooPublisherSubject.eraseToAnyPublisher() }
    public private(set) var fooPublisherSubject = PassthroughSubject<String, Never>()

    public var barPublisher: AnyPublisher<Int, CustomError> { return self.barPublisherSubject.eraseToAnyPublisher() }
    public private(set) var barPublisherSubject = CurrentValueSubject<Int, CustomError>(0)
}

You can also connect an AnyPublisher to a property within the protocol.

For example:

/// @mockable(combine: fooPublisher = @Published foo)
public protocol Foo {
    var foo: String { get }
    var fooPublisher: AnyPublisher<String, Never> { get }
}

This will generate:

public class FooMock: Foo {
    public init() { }
    public init(foo: String = "") {
        self.foo = foo
    }

    public private(set) var fooSetCallCount = 0
    @Published public var foo: String = "" { didSet { fooSetCallCount += 1 } }

    public var fooPublisher: AnyPublisher<String, Never> { return self.$foo.setFailureType(to: Never.self).eraseToAnyPublisher() }
}

Overrides

To override the generated mock name:

/// @mockable(override: name = FooMock)
public protocol FooProtocol { ... }

This will generate:

public class FooMock: FooProtocol { ... }

Used libraries

SwiftSyntax |

How to contribute to Mockolo

See CONTRIBUTING for more info.

Report any issues

If you run into any problems, please file a git issue. Please include:

  • The OS version (e.g. macOS 10.14.6)
  • The Swift version installed on your machine (from swift --version)
  • The Xcode version
  • The specific release version of this source code (you can use git tag to get a list of all the release versions or git log to get a specific commit sha)
  • Any local changes on your machine

License

Mockolo is licensed under Apache License 2.0. See LICENSE for more information.

Copyright (C) 2017 Uber Technologies

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

mockolo's People

Contributors

akkyie avatar alanzeino avatar andooown avatar aroshipup avatar espressocup avatar ffittschen avatar fummicc1 avatar ikesyo avatar ileitch avatar jimjag avatar kabirkhaan avatar maiyama18 avatar marciniwanicki avatar maxwelle avatar milettal avatar nk-5 avatar nonchalant avatar pablocornejo avatar pejato avatar rudro avatar ryanaveo avatar ryogabarbie avatar shindyu avatar sidepelican avatar tinder-matthewmourlam avatar tinder-maxwellelliott avatar uhooi avatar unicov avatar woxtu avatar yutailang0119 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mockolo's Issues

Swift Package Manager compiler errors

Creating an empty project and adding Mockolo does not build.

Swift Compiler Error Group
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:20:27: 'init(subsystem:category:)' is only available in iOS 10.0 or newer
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:20:17: Add @available attribute to enclosing let
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:46:9: 'os_signpost(_:dso:log:name:signpostID:)' is only available in iOS 12.0 or newer
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:46:9: Add 'if #available' version check
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:44:13: Add @available attribute to enclosing global function
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:46:22: 'begin' is only available in iOS 12.0 or newer
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:46:22: Add 'if #available' version check
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:44:13: Add @available attribute to enclosing global function
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:52:9: 'os_signpost(_:dso:log:name:signpostID:)' is only available in iOS 12.0 or newer
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:52:9: Add 'if #available' version check
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:50:13: Add @available attribute to enclosing global function
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:52:22: 'end' is only available in iOS 12.0 or newer
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:52:22: Add 'if #available' version check
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/Logger.swift:50:13: Add @available attribute to enclosing global function
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/TypeParser.swift:176:77: 'isEmoji' is only available in iOS 10.2 or newer
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/TypeParser.swift:176:77: Add 'if #available' version check
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/TypeParser.swift:167:9: Add @available attribute to enclosing property
/Users/adamshare/Library/Developer/Xcode/DerivedData/TestMockolo-elkqwoymahuitifkrfxpzgwqhipp/SourcePackages/checkouts/mockolo/Sources/MockoloFramework/Utils/TypeParser.swift:25:20: Add @available attribute to enclosing class

Remove sourcekitten from dependencies

@devturhan Are you still actively using mockolo via cocoapods? We're considering removing sourcekitten from dependencies as the other parser (swiftsyntax) is more performant and has been set to default for some time now. If removed, we won't be able to support mockolo via cocoapods any longer, as swiftsyntax is not a pod. You can still use mockolo via other means, mint, brew, etc (see README) but that means you only have access to the commandline, not the APIs. Let us know; if still actively used, we will instead update the pod version and continue to maintain.

Is private(set) really desirable?

We just upgraded our mockolo version and saw a bunch of build failures because we were resting the generated callCount properties during tests. This was necessary in our tests because of two reasons:

  1. To remove any unnecessary calls that happen during a test case setup
  2. If you mock a protocol with static methods, then you need to manually reset them between each test

Would it make sense to make this configurable in mockolo and for the case of the static method maybe even disable it by default?

Duplicate access level identifier for associated types

In the following scenario:

Generated mock file from module A

public class FooMock: Foo {
    public init() { }
    public init() {
    }

    public typealias T = String
}

Protocol declaration from module B

/// \(String.mockAnnotation)(typealias: T = String)
public protocol FooBar: Foo {
    associatedtype T
}

Results in

public class FooBarMock: FooBar {
    public init() { }

    public public typealias T = String
}

@elsh Any idea why the access level modifier is duplicated here?

Possibly redundant _doneInit

I am not sure if _doneInit variable in the generated class is needed. The only use-case I've seen so far was a computed variable when it was protecting the counter so it's not incremented on the initial set.

    private var _doneInit = false
    
    public init() { _doneInit = true }
    public init(currentDirectoryPath: AbsolutePath) {
        self.currentDirectoryPath = currentDirectoryPath
        _doneInit = true
    }
    public var currentDirectoryPathSetCallCount = 0
    var underlyingCurrentDirectoryPath: AbsolutePath! 
    public var currentDirectoryPath: AbsolutePath {
        get { return underlyingCurrentDirectoryPath }
        set {
            underlyingCurrentDirectoryPath = newValue
            if _doneInit { currentDirectoryPathSetCallCount += 1 }
        }
    }

but it seems, the same can be achieved by accessing the underlying variable directly.

    public init() {}
    public init(currentDirectoryPath: AbsolutePath) {
        underlyingCurrentDirectoryPath = currentDirectoryPath
    }
    public var currentDirectoryPathSetCallCount = 0
    var underlyingCurrentDirectoryPath: AbsolutePath! 
    public var currentDirectoryPath: AbsolutePath {
        get { return underlyingCurrentDirectoryPath }
        set {
            underlyingCurrentDirectoryPath = newValue
            currentDirectoryPathSetCallCount += 1
        }
    }

Few other things I'd consider:

  • replacing underlying prefix with _ to make it more obvious it's just an internal store, and making it private.
  • Adding some extra empty lines between the methods/variables to enhance readability.

Custom type property should not be private

I tried to generate a protocol that has computed/store properties use custom type. Mockolo will generate an Implicit Unwrap Optional property but mark it as private. Therefore no way to set value and Test will crash.

Example:

/// @mockable
protocol TestMock {
    var loadingConfig: AppLoadingConfig { get }
}

Mock:

class TestMockMock: TestMock {
    init() { }
    init(loadingConfig: AppLoadingConfig) {
        self._loadingConfig = loadingConfig
    }

    var loadingConfigetCallCount = 0
    private var _loadingConfig: AppLoadingConfig!  { didSet { loadingConfigSetCallCount += 1 } }
    var loadingConfig: AppLoadingConfig {
        get { return _loadingConfig }
        set { _loadingConfig = newValue }
    }
}

I think _loadingConfig should be mark as public.

Order of log levels

The current order of log levels:

public enum LogLevel: Int {
    case info
    case verbose
    case warning
    case error
}

Having those levels is nice as it allows the client code to decide how much information should be logged (or presented to the users).

The order of log levels is smoothly distributed between the most verbose (like verbose, trace or debug) and critical (error or critical etc).

I think the info and verbose cases could be swapped to allow uses to specify info if they want to know only general information about the generation process, or verbose if they the want to dig into details.

Avoid writing to destination file if content is unchanged

We recently investigated our build times and discovered that mockolo is always writing to the destination file even though the content did not change. This behavior will trigger Xcode to rebuild modules in which no source code was changed (and all of the modules that depend on this module). To improve this behavior, I'd suggest to check if the destination file already exists and only write the file if the content is unchanged.

To test the current behavior, I executed these commands:

stat Sources/Mocks.generated.swift
mockolo -s Sources/ -d Sources/Mocks.generated.swift -m DEBUG
stat Sources/Mocks.generated.swift

Looking at other tools that generate code, they also avoid to write the destination file if the content is unchanged. (See SwiftGen or R.swift)

If you accept this suggestion, I'd be happy to open a PR for this.

Support for `weak` mock properties

I would like to be able to specify that certain properties should be generated with the weak annotation. What would be a good way to add this to Mockolo's capabilities?

How to generate mock including where condition

protocol Parsable {
    //  ...
}
protocol APITarget {
    associatedtype ResultType

    // ...
}

/// @mockable
protocol Networking {
    func request<T>(_ target: T) -> T.ResultType where T: APITarget & Parsable
}

The above case, mockolo generates NetworkingMock like the below.

class NetworkingMock: Networking {
    init() { }


    private(set) var requestCallCount = 0
    var requestHandler: ((Any) -> (Any))?
    func request<T>(_ target: T) -> T.ResultType {
        requestCallCount += 1
        if let requestHandler = requestHandler {
            return requestHandler(target) as! T.ResultType
        }
        fatalError("requestHandler returns can't have a default value thus its handler must be set")
    }
}

The generated mock has an error about T does not have ResultType. It is because where conditions are missing in the mock.

How to generate mock including where conditions or is there an another solution?


My expected mock:

class NetworkingMock: Networking {
    init() { }


    private(set) var requestCallCount = 0
    var requestHandler: ((Any) -> (Any))?
    func request<T>(_ target: T) -> T.ResultType where T: APITarget & Parsable {  // added where conditions
        requestCallCount += 1
        if let requestHandler = requestHandler {
            return requestHandler(target) as! T.ResultType
        }
        fatalError("requestHandler returns can't have a default value thus its handler must be set")
    }
}

Add filelist support

We need a way to add input sources using a filelist for Xcode regeneration. We don't have a way of getting the root folder in that case (and the code for other targets may be mixed in).

Add --version

It'd be great to add a --version option to double check the installed version

lib_InternalSwiftSyntaxParser.dylib linking issue with Homebrew

I am having this issue with some laptops using Mockolo when running the homebrew version:

dyld: Library not loaded: @rpath/lib_InternalSwiftSyntaxParser.dylib
  Referenced from: /usr/local/bin/mockolo
  Reason: image not found

Strangely enough some laptops run this command totally fine, other break

Impossible to generate mocks by Xcode 12

Install mockolo using Xcode 12.0 following the below operation.

$ git clone https://github.com/uber/mockolo.git
$ cd mockolo
$ swift build -c release

To Generate mocks, run the below command, SwiftSyntax.ParserError is occurred. It is failed to generate mock.

.build/release/mockolo -s [MOCK TARGETS]
Fatal error: The operation couldn’t be completed. (SwiftSyntax.ParserError error 1.): file /Users/vagrant/git/mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/ParserViaSwiftSyntax.swift, line 86
/var/folders/6q/wgy6jtp12w5gzgm9lzcglpqw0000gn/T/bitrise706137049/step_src/._script_cont: line 43:  9133 Illegal instruction: 4  ./mockolo/.build/release/mockolo -s [MOCK TARGETS]

This issue may be occurred from Xcode 12.0 beta 1. ( latest beta version as well. )

Unknown attribute 'MockObservable'

The protocol contains Observable and generates a mock that gives an error. mockolo is installed with mint. Is there a way to resolve this situation?

  • protocol
@mockable
protocol SampleProtocol {
    var foo: Observable<String> { get }
}
  • mock
var fooSubjectSetCallCount: Int { return self._foo.callCount }
var fooSubject: PublishSubject<String> { return self._foo.publishSubject }
var fooReplaySubject: ReplaySubject<String> { return self._foo.replaySubject }
var fooBehaviorSubject: BehaviorSubject<String> { return self._foo.behaviorSubject }
@MockObservable(unwrapped: Observable<String>.empty()) var foo: Observable<String>  /// Unknown attribute 'MockObservable'

How to generate mock for protocol includes macro

Protocol includes macro, the method is not generated to mock result file. Is there a way to resolve this situation ?

protocol PresentableListener: class {
    func run()
    #if DEBUG
    func showDebugMode()
    #endif
}

If generate mock for the above protocol, Mock class includes run method but showDebugMode method is not generated.

Support for Combine

Add support for annotation attributes for Combine subjects and publishers so they too can be easily mocked.

Swift Package Manager dependency resolution fails

because no versions of mockolo match the requirement 1.2.4..<2.0.0 and package mockolo is required using a version-based requirement and it depends on unversion package swift-tools-support-core, mockolo >=1.2.3 is forbidden.
And because root depends on mockolo 1.2.3..<2.0.0, version solving failed

Support output as Directory

Input params -s --sourcedirs support scan all files in a folder/directory but the output will be merged into one file.

Could you support to generate into multiple files, map 1-1 with input files?

Add Project Status Disclaimer

In an effort to provide more transparency to project state, Add the appropriate disclaimer on the top of the README.

Some examples as follows:
-This project is a technical snapshot and will not be kept in sync.
-This project is deprecated and not maintained.
-This project is experimental and the APIs are not considered stable.
-This project is stable and being incubated for long-term support
-This project may contain experimental code and may not be ready for general use. Support and/or new releases may be limited.

Make `callCount` and `argValues` properties private(set) and the class final in generated mock

For e.g. instead of this

public class FooMock: Foo {
    init() {}
    init(num: Int = 0) {
        self.num = num
    }

    var numSetCallCount = 0
    var numArgValues = [Int]()
    var underlyingNum: Int = 0
    var num: Int {
        get {
            return underlyingNum
        }
        set {
            underlyingNum = newValue
            numArgValues.append(newValue)
            numSetCallCount += 1
        }
    }
}

It would nice if we could generate this where properties that should not be modified outside of the class are marked as private(set) and the class itself is marked as final.

public final class FooMock: Foo {
    init() {}
    init(num: Int = 0) {
        self.num = num
    }

    private(set) var numSetCallCount = 0
    private(set) var numArgValues = [Int]()
    private(set) var underlyingNum: Int = 0
    var num: Int {
        get {
            return underlyingNum
        }
        set {
            underlyingNum = newValue
            numArgValues.append(newValue)
            numSetCallCount += 1
        }
    }
}

Enhance function arguments history annotation syntax to support multiple functions

The ReadMe states that the following provides the ability to capture function arguments history:

/// @mockable(history: fooFunc = true)
public protocol Foo {
    func fooFunc(val: Int)
    func barFunc(_ val: (a: String, Float))
    func bazFunc(val1: Int, val2: String)
}

But if I wanted to track the history for multiple functions, say fooFunc and barFunc, there doesn't seem to be a way to convey that to Mockolo. For e.g. I thought something like this would work but it didn't (I tried a few different variations of the syntax but couldn't get it to work)

/// @mockable(history: fooFunc = true, barFunc = true)
public protocol Foo {
    func fooFunc(val: Int)
    func barFunc(_ val: (a: String, Float))
    func bazFunc(val1: Int, val2: String)
}

For now, I ended up using --enable-args-history but that paints with too broad a brush and it would be nice to have a syntax that can provide more granular control.

Support for Homebrew Formula

I have been investigating possibly allowing Mockolo to be installed via Homebrew. Are there any blockers to doing this? My strategy was to use the tarball creation script

./install-script.sh -s [source dir] -t mockolo -d [destination dir] -o [output filename]

To generate a tarball that the brew formula would install and then use homebrew's bin linking to make mockolo globally executable

Casting a closure when using generic closures

Mockolo is struggling to figure out the generic type if it is returned in a closure. If you give a parameter with a closure that is a Swift.Result with a T, Mockolo will successfully figure out the handler, but when consuming the handler it struggles to cast the handler correctly.

Consider this example:

/// @mockable
protocol ExampleProtocol {
    func foo<T>(
        bar: @escaping (String) throws -> T,
        completion: ((Result<T, Error>) -> Void)?
    )
}

Produces the generated mock below which does not compile with the follow error:
Cannot convert value of type '((Result<T, Error>) -> Void)?' to expected argument type '((Result<Any, Error>) -> Void)?'

class ExampleProtocolMock: ExampleProtocol {
    init() { }


    var fooCallCount = 0
    var fooHandler: ((@escaping (String) throws -> Any, ((Result<Any, Error>) -> Void)?) -> ())?
    func foo<T>(bar: @escaping (String) throws -> T, completion: ((Swift.Result<T, Error>) -> Void)?)  {
        fooCallCount += 1
        if let fooHandler = fooHandler {
            fooHandler(bar, completion) // completion needs to be casted here
        }
    }
}

To compile successfully I've manually added the casting of completion:

class ExampleProtocolMock: ExampleProtocol {
    init() { }


    var fooCallCount = 0
    var fooHandler: ((@escaping (String) throws -> Any, ((Result<Any, Error>) -> Void)?) -> ())?
    func foo<T>(bar: @escaping (String) throws -> T, completion: ((Result<T, Error>) -> Void)?)  {
        fooCallCount += 1
        if let fooHandler = fooHandler {
            fooHandler(bar, completion as? (Result<Any, Error>) -> Void)
        }

    }
}

A snippet of what the use case:

example.fooHandler = { name, result in
   print("Hello")
}

example.foo(
   bar: { name -> String in
      return name + " world"
    }) { (result) in
        switch result {
        case .success(let text): print(text)
        case .failure(let error): print(error.localizedDescription)
        }
 }

Add Variadic Parameters Support

Does not currently support Variadic Parameters in function signatures.

Protocol definition:
func example(strings: String...)

Expected mock:
var exampleHandler: ((_ strings: [String]) -> ())?

Actual mock:
var exampleHandler: ((_ strings: String) -> ())?

Header "-h" option does not work as intended

According to the documentation, "-h" should allow to specify header documentation (or even some imports if needed #84).

  --header, -h              A custom header documentation to be added to the beginning of a generated mock file.

Looks, it conflicts with the "help" command built into SwiftPM ArgumentParser, in which case the "help" command takes precedence and generation does not start.

Some possible solutions:

  • rename short name, i.e. --header, -hr
  • remove short name, i.e. --header (only)

Support imports wrapped in macros

Brought up in this PR: #82

We have the use-case where we have certain import statements wrapped in #if macros to only import them in e.g. debug builds. Mockolo is right now picking up every branch it would be great if it would retain the macros in the generated code as well.

Support methods with inout parameters

We have code that has a method with an inout parameter which generates invalid mock code.

Example:

public protocol Foo {
    func hash(into hasher: inout Hasher)
}

generates this:

public class FooMock: Foo {
    public var hashCallCount = 0
    public var hashHandler: ((inout Hasher) -> ())?
    public func hash(into hasher: inout Hasher)  {
        hashCallCount += 1
        if let hashHandler = hashHandler {
            hashHandler(hasher)
        }
        
    }
}

The generated code fails to compile

Is there any way to assert function argument histories?

I would't like to assert function argument histories like as bellow.

protocol Foo {
    func fooFunc(_ arg: Int)
    func barFunc(_ arg1: Int, arg2: String)
}

let mock = FooMock()
mock.fooFunc(1)
mock.fooFunc(2)
mock.fooFunc(3)

XCTAssertEqual(mock.fooFuncValues, [1, 2, 3])

mock.barFunc(1, "A")
mock.barFunc(2, "B")
mock.barFunc(3, "C")

XCTAssertEqual(mock.barFuncValues, [(1, "A"), (2, "B"), (3, "C")])

Is there any way to achive this? And if I can't do currently, can I contribute to implement this feature?

Specify module to avoid type ambiguity

Protocols with the same name can be available in multiple modules, i.e. FileSystem and other common symbols. SwiftPM FileSystem is a good example:

import TSCBasic // part of SwiftPM, with public `FileSystem` protocol

// Here, my own FileSystem protocol

@mockable
public protocol FileSystem {
   // ...
}

The generated mock for FileSystem protocol will not compile due to type ambiguity (the compiler will not know if we want to inherit from SwiftPM FileSystem or the one in our Framework).

One possible solution would be adding module attribute to @mockable annotation as such the generated mock could have more accurate inherit form <module>.<protocol> to avoid conflicts.

import TSCBasic // part of SwiftPM, with public `FileSystem` protocol

@mockable(module=MyFramework)
public protocol FileSystem {
   // ...
}
// GeneratedMocks.swift inside MyFrameworkMocks module

public class FileSytemMock: MyFramework.FileSystem {
   // ...
}

Duplicate generation of init

When defining a protocol with an init() mockolo generates two init()s

Example:

protocol Foo {
    init()
}

Output:

public class FooMock: Foo {
    public init() { 
    required public init() {

    }
}

Only one required init should be generated

Concurrency is not well balanced

Looking at a trace shows the concurrency is poorly balanced at the end of model generation and rendering:

Screen Shot 2019-07-24 at 8 43 34 PM

Most likely caused by large files, if so reverse sorting by size before processing could help.

Understanding the -mocks flag

I wanted to use the -mocks flag to avoid generating some mocks twice for a module that depended on another one. Maybe I misunderstood the usage of the flag. I have the following scenario:

ModuleA

import ModuleB

/// @mockable
public protocol ProtocolA: ProtocolB {}

ModuleB

/// @mockable
public protocol ProtocolB {
    func foo()
}

/// @mockable
protocol InternalProtocol {}

Now I run a generate command for both targets. For ModuleB everything is fine and it generated the right mocks. However in ModuleA it generated mocks for the InternalProtocol as well. However this fails since it cannot see the internal protocol and fails to compile.

I guess this is a pretty common scenario especially in the RIB use case as well where one has to import use the files of the dependencies to generate proper mocks but doesn't won't to generate mocks for them twice.

Am I using the -mocks option wrong or is there anything else I could use to reference files/folders which are scanned and used for inheritance etc. but not used to generate actual mocks. @elsh

Associated type protocol inheritance

A protocol inheriting from a protocol with associated type will also inherit any configured typealias types for its generated mock, e.g.:

/// @mockable(typealias: ContextType = Int; ObjectType = String)
protocol ProtocolA {
    associatedtype ContextType
    associatedtype ObjectType

    func createObject(withContext context: ContextType) -> ObjectType?
}

/// @mockable
protocol ContextProtocol {}

/// @mockable
protocol ObjectProtocol {}

/// @mockable(typealias: ContextType = ContextProtocol; ObjectType = ObjectProtocol)
protocol ProtocolB: ProtocolA where ContextType: ContextProtocol, ObjectType: ObjectProtocol {}

——————

final class ProtocolAMock: ProtocolA {
    typealias ContextType = Int
    typealias ContextType = String

    // Mock implementation…
}

final class ProtocolBMock: ProtocolB {
    typealias ContextType = Int // Not the expected/desired ContextProtocol
    typealias ContextType = String // Not the expected/desired ObjectProtocol

    // Mock implementation…
}

Cannot install with Mint

When trying to install Mockolo with Mint, the following error occurs:

$ mint bootstrap
🌱 Cloning mockolo 1.1.5
🌱 Resolving package
🌱 Building package
[1/13] Compiling clibc libc.c
[2/13] Compiling SourceKit SourceKit.m
[3/14] Compiling atomic-counter.c
[4/14] Compiling Clang_C Clang_C.m
[5/14] Compiling SPMLibc libc.swift
[6/15] Compiling writer.c
[7/15] Compiling reader.c
[8/16] Compiling api.c
[9/16] Compiling parser.c
[10/16] Compiling emitter.c
[11/16] Compiling scanner.c
[12/17] Compiling SWXMLHash SWXMLHash.swift
[13/17] Compiling Basic Await.swift
[14/18] Compiling Yams Constructor.swift
[15/19] Compiling SPMUtility ArgumentParser.swift
[16/19] Compiling SourceKittenFramework ByteCount.swift
[17/19] Compiling SwiftSyntax AbsolutePosition.swift
[18/20] Archiving libSwiftSyntax.a
[19/20] Compiling MockoloFramework ClassModel.swift
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/ParserViaSwiftSyntax.swift:72:18: error: value of type 'SourceFileSyntax' has no member 'walk'
            node.walk(&treeVisitor)
            ~~~~ ^~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:576:10: error: overriding declaration requires an 'override' keyword
    func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {
         ^
    override 
SwiftSyntax.SyntaxVisitor:190:15: note: overridden declaration is here
    open func visit(_ node: SwiftSyntax.ProtocolDeclSyntax) -> SwiftSyntax.SyntaxVisitorContinueKind
              ^
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:584:10: error: overriding declaration requires an 'override' keyword
    func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
         ^
    override 
SwiftSyntax.SyntaxVisitor:186:15: note: overridden declaration is here
    open func visit(_ node: SwiftSyntax.ClassDeclSyntax) -> SwiftSyntax.SyntaxVisitorContinueKind
              ^
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:599:10: error: overriding declaration requires an 'override' keyword
    func visit(_ node: ImportDeclSyntax) -> SyntaxVisitorContinueKind {
         ^
    override 
SwiftSyntax.SyntaxVisitor:222:15: note: overridden declaration is here
    open func visit(_ node: SwiftSyntax.ImportDeclSyntax) -> SwiftSyntax.SyntaxVisitorContinueKind
              ^
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:149:38: warning: cast from 'DeclSyntax' to unrelated type 'VariableDeclSyntax' always fails
        if let varMember = self.decl as? VariableDeclSyntax {
                           ~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:156:46: warning: cast from 'DeclSyntax' to unrelated type 'FunctionDeclSyntax' always fails
        } else if let funcMember = self.decl as? FunctionDeclSyntax {
                                   ~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:162:51: warning: cast from 'DeclSyntax' to unrelated type 'SubscriptDeclSyntax' always fails
        } else if let subscriptMember = self.decl as? SubscriptDeclSyntax {
                                        ~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:168:46: warning: cast from 'DeclSyntax' to unrelated type 'InitializerDeclSyntax' always fails
        } else if let initMember = self.decl as? InitializerDeclSyntax {
                                   ~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:174:45: warning: cast from 'DeclSyntax' to unrelated type 'AssociatedtypeDeclSyntax' always fails
        } else if let patMember = self.decl as? AssociatedtypeDeclSyntax {
                                  ~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~~~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:178:44: warning: cast from 'DeclSyntax' to unrelated type 'TypealiasDeclSyntax' always fails
        } else if let taMember = self.decl as? TypealiasDeclSyntax {
                                 ~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:182:49: warning: cast from 'DeclSyntax' to unrelated type 'IfConfigDeclSyntax' always fails
        } else if let ifMacroMember = self.decl as? IfConfigDeclSyntax {
                                      ~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:194:44: warning: cast from 'DeclSyntax' to unrelated type 'VariableDeclSyntax' always fails
            if let varMember = member.decl as? VariableDeclSyntax {
                               ~~~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:233:77: warning: cast from 'Syntax' to unrelated type 'MemberDeclListSyntax' always fails
            if let desc = cl.condition?.description, let list = cl.elements as? MemberDeclListSyntax {
                                                                ~~~~~~~~~~~ ^   ~~~~~~~~~~~~~~~~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:248:64: error: value of type 'IfConfigDeclSyntax' has no member 'offset'
        let macroModel = IfMacroModel(name: name, offset: self.offset, entities: subModels)
                                                          ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:372:52: error: value of type 'PatternBindingSyntax' has no member 'offset'
                                         offset: v.offset,
                                                 ~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:373:52: error: value of type 'PatternBindingSyntax' has no member 'length'
                                         length: v.length,
                                                 ~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:402:55: error: value of type 'SubscriptDeclSyntax' has no member 'offset'
                                         offset: self.offset,
                                                 ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:403:55: error: value of type 'SubscriptDeclSyntax' has no member 'length'
                                         length: self.length,
                                                 ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:430:50: error: value of type 'FunctionDeclSyntax' has no member 'offset'
                                    offset: self.offset,
                                            ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:431:50: error: value of type 'FunctionDeclSyntax' has no member 'length'
                                    length: self.length,
                                            ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:469:41: error: value of type 'InitializerDeclSyntax' has no member 'offset'
                           offset: self.offset,
                                   ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:470:41: error: value of type 'InitializerDeclSyntax' has no member 'length'
                           length: self.length,
                                   ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:486:40: error: value of type 'GenericParameterSyntax' has no member 'offset'
                          offset: self.offset,
                                  ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:487:40: error: value of type 'GenericParameterSyntax' has no member 'length'
                          length: self.length)
                                  ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:523:40: error: value of type 'FunctionParameterSyntax' has no member 'offset'
                          offset: self.offset,
                                  ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:524:40: error: value of type 'FunctionParameterSyntax' has no member 'length'
                          length: self.length)
                                  ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:540:44: error: value of type 'AssociatedtypeDeclSyntax' has no member 'offset'
                              offset: self.offset,
                                      ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:541:44: error: value of type 'AssociatedtypeDeclSyntax' has no member 'length'
                              length: self.length,
                                      ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:555:44: error: value of type 'TypealiasDeclSyntax' has no member 'offset'
                              offset: self.offset,
                                      ~~~~ ^~~~~~
/private/var/folders/8z/2fgw5gmd1s5dxg45xf_gxl1j3w9szh/T/mint/github.com_uber_mockolo/Sources/MockoloFramework/Parsers/ViaSwiftSyntax/SwiftSyntaxExtensions.swift:556:44: error: value of type 'TypealiasDeclSyntax' has no member 'length'
                              length: self.length,
                                      ~~~~ ^~~~~~
🌱 Encountered error during "swift build -c release -Xswiftc -target -Xswiftc x86_64-apple-macosx10.14". Use --verbose to see full output
🌱  Failed to build mockolo 1.1.5 with SPM

How can I solve it?


Environments

Versions

  • Mint: 0.14.1
  • Mockolo: 1.1.5

Mintfile

Environment Variables

  • MINT_PATH: /Users/{user name}/.mint/lib
  • MINT_LINK_PATH: /Users/{user name}/.mint/bin

Optional Generic not generated correctly

With the following definition

/// @mockable
public protocol Github {
    func nonOptional<T>() -> T
    func optional<T>() -> T?
}

The generated output will be

public class ProtocolMock: Protocol {

    public var nonOptionalCallCount = 0
    public var nonOptionalHandler: (() -> (Any))?
    public func nonOptional<T>() -> T {
        ...
    }
    public var optionalCallCount = 0
    public var optionalHandler: (() -> (T?))?
    public func optional<T>() -> T? {
        ...
    }
}

The non optional method correctly generated T as Any while with the optional it is generated as T? this causes a compilation error Use of undeclared type 'T'.

Ability to add custom imports

It would be great to have the ability to add custom imports which can be passes trough a command line option.

Background:
Let's say we have module A and the respective test target ATests. If we want to put the generated mocks from A into the ATests target it will miss the @testable import A at the top and won't compile.

Maybe you could also explain where you suggest to put the generated mocks. Into the target A or the test target ATests or rather into a separate target. Would be great if you can share some best practices from your experience.

Especially when you need to use mocks from different modules to write a unit test.

Performance improvements

There appear to be some performance issues when parsing dependent mocks as compared to generating all in one batch. We should profile and optimise to improve this.

Ref T3509083

CocoaPods Support

Hi

I would like to implement CocoaPods support for the mockolo.
Is it possible for you to add zipped executable to the release files?

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.