Boilerplate-free mocking framework for Swift!

Overview

Cuckoo

Mock your Swift objects!

CI Status Version SwiftPM compatible Carthage compatible License Platform

Introduction

Cuckoo was created due to lack of a proper Swift mocking framework. We built the DSL to be very similar to Mockito, so anyone coming from Java/Android can immediately pick it up and use it.

To have a chat, join our Slack workspace!

How does it work

Cuckoo has two parts. One is the runtime and the other one is an OS X command-line tool simply called CuckooGenerator.

Unfortunately Swift does not have a proper reflection, so we decided to use a compile-time generator to go through files you specify and generate supporting structs/classes that will be used by the runtime in your test target.

The generated files contain enough information to give you the right amount of power. They work based on inheritance and protocol adoption. This means that only overridable things can be mocked. Due to the complexity of Swift it is not easy to check for all edge cases so if you find some unexpected behavior, please file an issue.

Changelog

List of all changes and new features can be found here.

Features

Cuckoo is a powerful mocking framework that supports:

  • inheritance (grandparent methods)
  • generics
  • simple type inference for instance variables (works with initializers, as TYPE notation, and can be overridden by specifying type explicitly)
  • Objective-C mocks utilizing OCMock

What will not be supported

Due to the limitations mentioned above, unoverridable code structures are not supportable by Cuckoo. This includes:

  • struct - workaround is to use a common protocol
  • everything with final or private modifier
  • global constants and functions
  • static properties and methods

Requirements

Cuckoo works on the following platforms:

  • iOS 8+
  • Mac OSX 10.9+
  • tvOS 9+

watchOS support is not yet possible due to missing XCTest library.

Note: Version 1.2.0 is the last one supporting Swift 4.2. Use versions 1.3.0+ for Swift 5 and up.

Cuckoo

1. Installation

CocoaPods

Cuckoo runtime is available through CocoaPods. To install it, simply add the following line to your test target in your Podfile:

pod "Cuckoo"

And add the following Run script build phase to your test target's Build Phases above the Compile Sources phase:

# Define output file. Change "${PROJECT_DIR}/${PROJECT_NAME}Tests" to your test's root source folder, if it's not the default name.
OUTPUT_FILE="${PROJECT_DIR}/${PROJECT_NAME}Tests/GeneratedMocks.swift"
echo "Generated Mocks File = ${OUTPUT_FILE}"

# Define input directory. Change "${PROJECT_DIR}/${PROJECT_NAME}" to your project's root source folder, if it's not the default name.
INPUT_DIR="${PROJECT_DIR}/${PROJECT_NAME}"
echo "Mocks Input Directory = ${INPUT_DIR}"

# Generate mock files, include as many input files as you'd like to create mocks for.
"${PODS_ROOT}/Cuckoo/run" generate --testable "${PROJECT_NAME}" \
--output "${OUTPUT_FILE}" \
"${INPUT_DIR}/FileName1.swift" \
"${INPUT_DIR}/FileName2.swift" \
"${INPUT_DIR}/FileName3.swift"
# ... and so forth, the last line should never end with a backslash

# After running once, locate `GeneratedMocks.swift` and drag it into your Xcode test target group.

IMPORTANT: To make your mocking journey easier, make absolutely sure that the run script is above the Compile Sources phase.

NOTE: To avoid race condition errors when Xcode parallelizes build phases, add the path of the OUTPUT_FILE into the "Output Files" section of the build phase. If you find that OUTPUT_FILE still doesn't regenerate with new changes, adding mocked files to the "Input Files" section of the build phase might help.

Input files can be also specified directly in Run script in Input Files form.

Note: All paths in the Run script must be absolute. Variable PROJECT_DIR automatically points to your project directory. Remember to include paths to inherited Classes and Protocols for mocking/stubbing parent and grandparents.

Swift Package Manager

To use Cuckoo with Apple's Swift package manager, add the following as a dependency to your Package.swift:

.package(url: "https://github.com/Brightify/Cuckoo.git", .upToNextMajor(from: "1.3.0"))

after that add "Cuckoo" as a dependency of the test target.

If you're unsure, take a look at this example PackageDescription:

// swift-tools-version:4.0
import PackageDescription

let package = Package(
    name: "Friendlyst",
    products: [
        .library(
            name: "Friendlyst",
            targets: ["Friendlyst"]),
    ],
    dependencies: [
        .package(url: "https://github.com/Brightify/Cuckoo.git", .upToNextMajor(from: "1.3.0"))
    ],
    targets: [
        .target(
            name: "Friendlyst",
            dependencies: [],
            path: "Source"),
        .testTarget(
            name: "FriendlystTests",
            dependencies: ["Cuckoo"],
            path: "Tests"),
    ]
)

Cuckoo relies on a script that is currently not downloadable using SwiftPM. However, for convenience, you can copy this line into the terminal to download the latest run script. Unfortunately if there are changes in the run script, you'll need to execute this line again.

curl -Lo run https://raw.githubusercontent.com/Brightify/Cuckoo/master/run && chmod +x run

When you're all set, use the same Run script phase as above and replace

"${PODS_ROOT}/Cuckoo/run"

with

"${PROJECT_DIR}/run" --download

The --download option is necessary because the Generator sources are not cloned in your project (they're in DerivedData, out of reach). You can add a version (e.g. 1.3.0) after it to get a specific version of the cuckoo_generator. Use --clean as well to replace the current cuckoo_generator if you're changing versions.

Carthage

To use Cuckoo with Carthage add this line to your Cartfile:

github "Brightify/Cuckoo"

Then use the Run script from above and replace

"${PODS_ROOT}/Cuckoo/run"

with

"Carthage/Checkouts/Cuckoo/run"

Don't forget to add the Framework into your project.

2. Usage

Usage of Cuckoo is similar to Mockito and Hamcrest. However, there are some differences and limitations caused by generating the mocks and Swift language itself. List of all the supported features can be found below. You can find complete examples in tests.

Mock initialization

Mocks can be created with the same constructors as the mocked type. Name of mock class always corresponds to name of the mocked class/protocol with Mock prefix (e.g. mock of protocol Greeter is called MockGreeter).

let mock = MockGreeter()

Spy

Spies are a special case of Mocks where each call is forwarded to the victim by default. Since Cuckoo version 0.11.0 we changed the way spies work. When you need a spy, give Cuckoo a class to mock instead of a protocol. You'll then be able to call enableSuperclassSpy() (or withEnabledSuperclassSpy()) on a mock instance and it will behave like a spy for the parent class.

let spy = MockGreeter().withEnabledSuperclassSpy()

Note: The behavior was changed due to a limitation of Swift. Since we can't create a real proxy for the spy, calls inside the spy were not caught by the Mock and it was confusing. If you rely on the old behavior (i.e. you use spies with final classes), let us know on Slack or create an issue.

Stubbing

Stubbing can be done by calling methods as a parameter of the when function. The stub call must be done on special stubbing object. You can get a reference to it with the stub function. This function takes an instance of the mock that you want to stub and a closure in which you can do the stubbing. The parameter of this closure is the stubbing object.

Note: It is currently possible for the subbing object to escape from the closure. You can still use it to stub calls but it is not recommended in practice as the behavior of this may change in the future.

After calling the when function you can specify what to do next with following methods:

/// Invokes `implementation` when invoked.
then(_ implementation: IN throws -> OUT)

/// Returns `output` when invoked.
thenReturn(_ output: OUT, _ outputs: OUT...)

/// Throws `error` when invoked.
thenThrow(_ error: ErrorType, _ errors: Error...)

/// Invokes real implementation when invoked.
thenCallRealImplementation()

/// Does nothing when invoked.
thenDoNothing()

The available methods depend on the stubbed method characteristics. For example you cannot use the thenThrow method with a method that isn't throwing or rethrowing.

An example of stubbing a method looks like this:

stub(mock) { stub in
  when(stub.greetWithMessage("Hello world")).then { message in
    print(message)
  }
}

As for a property:

stub(mock) { stub in
  when(stub.readWriteProperty.get).thenReturn(10)
  when(stub.readWriteProperty.set(anyInt())).then {
    print($0)
  }
}

Notice the get and set, these will be used in verification later.

Enabling default implementation

In addition to stubbing, you can enable default implementation using an instance of the original class that's being mocked. Every method/property that is not stubbed will behave according to the original implementation.

Enabling the default implementation is achieved by simply calling the provided method:

let original = OriginalClass<Int>(value: 12)
mock.enableDefaultImplementation(original)

For passing classes into the method, nothing changes whether you're mocking a class or a protocol. However, there is a difference if you're using a struct to conform to the original protocol we are mocking:

let original = ConformingStruct<String>(value: "Hello, Cuckoo!")
mock.enableDefaultImplementation(original)
// or if you need to track changes:
mock.enableDefaultImplementation(mutating: &original)

Note that this only concerns structs. enableDefaultImplementation(_:) and enableDefaultImplementation(mutating:) are different in state tracking.

The standard non-mutating method enableDefaultImplementation(_:) creates a copy of the struct for default implementation and works with that. However, the mutating method enableDefaultImplementation(mutating:) takes a reference to the struct and the changes of the original are reflected in the default implementation calls even after enabling default implementation.

We recommend using the non-mutating method for enabling default implementation unless you need to track the changes for consistency within your code.

Chain stubbing

It is possible to chain stubbing. This is useful for when you need to define different behavior for multiple calls in order. The last behavior will be used for all calls after that. The syntax goes like this:

when(stub.readWriteProperty.get).thenReturn(10).thenReturn(20)

which is equivalent to:

when(stub.readWriteProperty.get).thenReturn(10, 20)

The first call to readWriteProperty will return 10 and all calls after that will return 20.

You can combine the stubbing methods as you like.

Overriding of stubbing

When looking for stub match Cuckoo gives the highest priority to the last call of when. This means that calling when multiple times with the same function and matchers effectively overrides the previous call. Also more general parameter matchers have to be used before specific ones.

when(stub.countCharacters(anyString())).thenReturn(10)
when(stub.countCharacters("a")).thenReturn(1)

In this example calling countCharacters with a will return 1. If you reversed the order of stubbing then the output would always be 10.

Usage in real code

After previous steps the stubbed method can be called. It is up to you to inject this mock into your production code.

Note: Call on mock which wasn't stubbed will cause an error. In case of a spy, the real code will execute.

Verification

For verifying calls there is function verify. Its first parameter is the mocked object, optional second parameter is the call matcher. Then the call with its parameters follows.

verify(mock).greetWithMessage("Hello world")

Verification of properties is similar to their stubbing.

You can check if there are no more interactions on mock with function verifyNoMoreInteractions.

With Swift's generic types, it is possible to use a generic parameter as the return type. To properly verify these methods, you need to be able to specify the return type.

// Given:
func genericReturn<T: Codable>(for: String) -> T? { ... }

// Verify
verify(mock).genericReturn(for: any()).with(returnType: String?.self)
Argument capture

You can use ArgumentCaptor to capture arguments in verification of calls (doing that in stubbing is not recommended). Here is an example code:

mock.readWriteProperty = 10
mock.readWriteProperty = 20
mock.readWriteProperty = 30

let argumentCaptor = ArgumentCaptor<Int>()
verify(mock, times(3)).readWriteProperty.set(argumentCaptor.capture())
argumentCaptor.value // Returns 30
argumentCaptor.allValues // Returns [10, 20, 30]

As you can see, method capture() is used to create matcher for the call and then you can get the arguments via properties value and allValues. value returns last captured argument or nil if none. allValues returns array with all captured values.

3. Matchers

Cuckoo makes use of matchers to connect your mocks to your code under test.

A) Automatic matchers for known types

You can mock any object that conforms to the Matchable protocol. These basic values are extended to conform to Matchable:

  • Bool
  • String
  • Float
  • Double
  • Character
  • Int
  • Int8
  • Int16
  • Int32
  • Int64
  • UInt
  • UInt8
  • UInt16
  • UInt32
  • UInt64

Matchers for Array, Dictionary, and Set are automatically synthesized as long as the type of the element conforms to Matchable.

B) Custom matchers

If Cuckoo doesn't know the type you are trying to compare, you have to write your own method equal(to:) using a ParameterMatcher. Add this method to your test file:

func equal(to value: YourCustomType) -> ParameterMatcher<YourCustomType> {
  return ParameterMatcher { tested in
    // Implementation of equality test for your custom type.
  }
}

⚠️ Trying to match an object with an unknown or non-Matchable type may lead to:

Command failed due to signal: Segmentation fault: 11

For details or an example (with Alamofire), see this issue.

Parameter matchers

ParameterMatcher itself also conforms to Matchable. You can create your own ParameterMatcher instances or if you want to directly use your custom types there is the Matchable protocol. Standard instances of ParameterMatcher can be obtained via these functions:

/// Returns an equality matcher.
equal<T: Equatable>(to value: T)

/// Returns an identity matcher.
equal<T: AnyObject>(to value: T)

/// Returns a matcher using the supplied function.
equal<T>(to value: T, equalWhen equalityFunction: (T, T) -> Bool)

/// Returns a matcher matching any Int value.
anyInt()

/// Returns a matcher matching any String value.
anyString()

/// Returns a matcher matching any T value or nil.
any<T>(type: T.Type = T.self)

/// Returns a matcher matching any closure.
anyClosure()

/// Returns a matcher matching any throwing closure.
anyThrowingClosure()

/// Returns a matcher matching any non nil value.
notNil()

Cuckoo also provides plenty of convenience matchers for sequences and dictionaries, allowing you to check if a sequence is a superset of a certain sequence, contains at least one of its elements, or is completely disjunct from it.

Matchable can be chained with methods or and and like so:

verify(mock).greetWithMessage("Hello world".or("Hallo Welt"))

Call matchers

As a second parameter of the verify function you can use instances of CallMatcher. Its primary function is to assert how many times was the call made. But the matches function has a parameter of type [StubCall] which means you can use a custom CallMatcher to inspect the stub calls or for some side effect.

Note: Call matchers are applied after the parameter matchers. So you get only stub calls of the desired method with correct arguments.

Standard call matchers are:

/// Returns a matcher ensuring a call was made `count` times.
times(_ count: Int)

/// Returns a matcher ensuring no call was made.
never()

/// Returns a matcher ensuring at least one call was made.
atLeastOnce()

/// Returns a matcher ensuring call was made at least `count` times.
atLeast(_ count: Int)

/// Returns a matcher ensuring call was made at most `count` times.
atMost(_ count: Int)

As with Matchable you can chain CallMatcher with methods or and and. However, you can't mix Matchable and CallMatcher together.

Resetting mocks

Following functions are used to reset stubbing and/or invocations on mocks.

/// Clears all invocations and stubs of given mocks.
reset<M: Mock>(_ mocks: M...)

/// Clears all stubs of given mocks.
clearStubs<M: Mock>(_ mocks: M...)

/// Clears all invocations of given mocks.
clearInvocations<M: Mock>(_ mocks: M...)

Stub objects

Stubs are used for suppressing real code. Stubs are different from Mocks in that they don't support stubbing nor verification. They can be created with the same constructors as the mocked type. Name of stub class always corresponds to name of the mocked class/protocol with Stub suffix (e.g. stub of protocol Greeter is called GreeterStub).

let stub = GreeterStub()

When a method is called or a property accessed/set on a stub, nothing happens. If a value is to be returned from a method or a property, DefaultValueRegistry provides a default value. Stubs can be used to set implicit (no) behavior to mocks without the need to use thenDoNothing() like this: MockGreeter().spy(on: GreeterStub()).

DefaultValueRegistry

DefaultValueRegistry is used by Stubs to get default values for return types. It knows only default Swift types, sets, arrays, dictionaries, optionals, and tuples (up to 6 values). Tuples for more values can be added through extensions. Custom types must be registered before use with DefaultValueRegistry.register<T>(value: T, forType: T.Type). Furthermore, default values set by Cuckoo can also be overridden by this method. Sets, arrays, etc. do not have to be registered if their generic type is already registered.

DefaultValueRegistry.reset() returns the registry to its clean slate before the register method made any changes.

Type inference

Cuckoo does a simple type inference on all variables which allows for much cleaner source code on your side. There are a total 3 ways the inference tries to extract the type name from a variable:

// From the explicitly declared type:
let constant1: MyType

// From the initial value:
let constant2 = MyType(...)

// From the explicitly specified type `as MyType`:
let constant3 = anything as MyType

Cuckoo generator

Installation

For normal use you can skip this because the run script downloads or builds the correct version of the generator automatically.

Custom

So you have chosen a more complicated path. You can clone this repository and build it yourself. Take a look at the run script for more inspiration.

Usage

Generator can be executed manually through the terminal. Each call consists of build options, a command, generator options, and arguments. Options and arguments depend on the command used. Options can have additional parameters. Names of all of them are case sensitive. The order goes like this:

cuckoo build_options command generator_options arguments

Build Options

These options are only used for downloading or building the generator and don't interfere with the result of the generated mocks.

When the run script is executed without any build options (they are only valid when specified BEFORE the command), it simply searches for the cuckoo_generator file and builds it from source code if it's missing.

To download the generator from GitHub instead of building it, use the --download [version] option as the first argument (i.e. run --download generate ... or run --download 1.3.0 generate ... to fetch a specific version). If you're having issues with rather long build time (especially in CI), this might be the way to fix it.

NOTE: If you encounter Github API rate limit using the --download option, the run script refers to the environment variable GITHUB_ACCESS_TOKEN. Add this line (replacing the Xs with your GitHub token, no additional permissions are needed) to the script build phase above the run call:

export GITHUB_ACCESS_TOKEN="XXXXXXX"

The build option --clean forces either build or download of the version specified even if the generator is present. At the moment the run script doesn't enforce the generator version to be the same as the Cuckoo version. We recommend using this option after updating Cuckoo as well as if you're having mysterious compile errors in the generated mocks. Please try to use this option first to verify that your generator isn't outdated before filing an issue about incorrectly generated mocks.

We recommend only using --clean when you're trying to fix a compile problem as it forces the build (or download) every time which makes the testing way longer than it needs to be.

Generator commands

generate command

Generates mock files.

This command accepts options that can be used to adjust the behavior of the generator, these are listed below.

After the options come arguments, in this case a list (separated by spaces) of files for which you want to generate mocks or that are required for correct inheritance mocking.

--output (string)

Absolute path to where the generated mocks will be stored.

If a path to a directory is supplied, each input file will be mapped to its own output file with mocks.

If a path to a file is supplied, all mocks will be generated into this single file.

The default value is GeneratedMocks.swift.

--testable (string)[,(string)...]

A comma separated list of frameworks that should be imported as @testable in the mock files.

--exclude (string)[,(string)...]

A comma separated list of classes and protocols that should be skipped during mock generation.

--no-header

Do not generate file headers.

--no-timestamp

Do not generate timestamp.

--no-inheritance

Do not mock/stub parents and grandparents.

--file-prefix (string)

Names of generated files in directory will start with this prefix. Only works when output path is directory.

--no-class-mocking

Do not generate mocks for classes.

--regex (string)

A regular expression pattern that is used to match Classes and Protocols. All that do not match are excluded. Can be used alongside --exclude in which case the --exclude has higher priority.

-g or --glob

Activate glob parsing for specified input paths.

-d or --debug

Run generator in debug mode. There is more info output as well as included in the generated mocks (e.g. method parameter info).

version command

Prints the version of this generator.

help command

Display general or command-specific help.

After the help command you can specify the name of another command for displaying command-specific information.

Objective-C Support

Cuckoo subspec Cuckoo/OCMock brings support for mocking Objective-C classes and protocols.

Example usage:

let tableView = UITableView()
// stubbing the class is very similar to stubbing with Cuckoo
let mock = objcStub(for: UITableViewController.self) { stubber, mock in
  stubber.when(mock.numberOfSections(in: tableView)).thenReturn(1)
  stubber.when(mock.tableView(tableView, accessoryButtonTappedForRowWith: IndexPath(row: 14, section: 2))).then { args in
    // `args` is [Any] of the arguments passed and the closure needs to cast them manually
    let (tableView, indexPath) = (args[0] as! UITableView, args[1] as! IndexPath)
    print(tableView, indexPath)
  }
}

// calling stays the same
XCTAssertEqual(mock.numberOfSections(in: tableView), 1)
mock.tableView(tableView, accessoryButtonTappedForRowWith: IndexPath(row: 14, section: 2))

// `objcVerify` is used to verify the interaction with the methods/variables
objcVerify(mock.numberOfSections(in: tableView))
objcVerify(mock.tableView(tableView, accessoryButtonTappedForRowWith: IndexPath(row: 14, section: 2)))

Detailed usage is available in Cuckoo tests along with DOs and DON'Ts of this Swift-ObjC bridge.

So far, only CocoaPods is supported. To install, simply add this line to your Podfile:

pod 'Cuckoo/OCMock'

Contribute

Cuckoo is open for everyone and we'd like you to help us make the best Swift mocking library. For Cuckoo development, follow these steps:

  1. Make sure you have latest stable version of Xcode installed
  2. Clone the Cuckoo repository
  3. In Terminal, run: make dev from inside the Cuckoo directory
  4. Open Cuckoo.xcodeproj
  5. Select either Cuckoo-iOS or Cuckoo-macOS scheme and verify by running the tests (⌘+U)
  6. Peek around or file a pull request with your changes

The project consists of two parts - runtime and code generator. When you open the Cuckoo.xcodeproj in Xcode, you'll see these directories: - Source - runtime sources - Tests - tests for the runtime part - CuckoGenerator.xcodeproj - project generated by make dev containing Generator source code

Thank you for your help!

Inspiration

Used libraries

License

Cuckoo is available under the MIT License.

Comments
  • Builds failing on Bitrise with stack Xcode 13.3.x (beta)

    Builds failing on Bitrise with stack Xcode 13.3.x (beta)

    Bitrise stack "Xcode 13.3.x (beta) on macOS 12.2 (Monterey)" is failing builds with the following error message

    Last lines of the build log:
    Script path: /Users/vagrant/git/CRM
    xargs: /Users/vagrant/git/CRM/cuckoo_generator: terminated with signal 4; aborting
    Command PhaseScriptExecution failed with a nonzero exit code
    
    Test session results, code coverage, and logs:
    	/var/folders/62/0p2cg52j6r16xjxfqch4vgt40000gn/T/XCUITestOutput573379856/Test.xcresult
    
    Testing failed:
    	Command PhaseScriptExecution failed with a nonzero exit code
    	Testing cancelled because the build failed.
    
    ** TEST FAILED **
    
    The following build commands failed:
    	PhaseScriptExecution Cuckoo\ Run\ Script /Users/vagrant/Library/Developer/Xcode/DerivedData/CRM-gfmkxxwjwsejasaegkkqxuwbswfa/Build/Intermediates.noindex/CRM.build/Debug-iphonesimulator/CRMTests.build/Script-3312E990D1E37BD0916B2578.sh (in target 'CRMTests' from project 'CRM')
    (1 failure)
    

    Recently included the following conditionals in the Cuckoo Run Script which seems to have no improvement

    if [ $ACTION == "indexbuild" ]; then
      echo "Not running Cuckoo generator during indexing."
      exit 0 
    fi
    
    # Skip for preview builds
    if [ "${ENABLE_PREVIEWS}" = "YES" ]; then
      echo "Not running Cuckoo generator during preview builds."
      exit 0
    fi
    
    bug 
    opened by brandtdaniels 37
  • "Overriding property must be as accessible as its enclosing type" while generating mock file

    I am using Xcode 11.3 and swift 5. Mac - 10.14.6 Mojave

    I am using Cuckoo "1.1.1" and I start getting below error. I resolved the error using suggestion but once you build it will show again.

    Screenshot 2019-10-22 at 4 55 23 PM

    2- One more issue is:

    Value of type 'Class' has no member 'function': even though function has the internal access level.

    If anybody else facing same issue? please help.

    bug awaiting reply 
    opened by sanoj00b 31
  • GeneratedMocks modified during Build Phase Xcode 13

    GeneratedMocks modified during Build Phase Xcode 13

    Starting in Xcode 13 we started getting errors around Cuckoo that the GeneratedMocks file was modified during build time. It seems like this could be an Xcode 13 issue as it doesn't seem to be respecting Output Files, but is there anything you guys can do on your end to fix this? Or do you know of any tips for fixing this on the integration side? We've been using Cuckoo for a couple years and have never had issues until Xcode 13. We are using input files lists for the list of mocks we want and an output file for the GeneratedMocks class. Our build phase is right before the Compile Sources build phase.

    opened by bratxdy 19
  • Missed methods for protocol with multiple inheritance

    Missed methods for protocol with multiple inheritance

    I have protocol: BudgetPresenterDelegate which is inherited from BasePresenterDelegate.

    protocol BasePresenterDelegate: class {
        func showPage(title: String?)
        func showTabBar(title: String, image: UIImage, selected: UIImage)
        func showMessage(with title: String, _ message: String, _ actions: [UIAlertAction])
    }
    
    protocol BudgetPresenterDelegate: BasePresenterDelegate, CreateSearchTableViewHeaderDataSource {
        func cancelSearch()
        func showGroupList()
        func removeBottomOffset()
        func setBottomOffset(_ offset: Double)
        func refreshData(for mode: BudgetHeaderMode)
        func showCreateNewGroupMessage(message: NSAttributedString)
        func createBudgetCell(with title: String?) -> UITableViewCell?
    }
    

    In the generated file MockBudgetDetailPresenterDelegate I'll have next error:

    Type 'MockBudgetDetailPresenterDelegate' does not conform to protocol 'BasePresenterDelegate'
    
    opened by denys-meloshyn 17
  • "Illegal instruction: 4" error

    Hi! thanks for this awesome framework. Today (without no action that I can remember) I have started experiencing this error in the cuckoo generator script: "Illegal instruction: 4" The complete error is:

    MyProject/Pods/Cuckoo/run: line 18: 72872 Illegal instruction: 4 $FILE_PATH/Contents/MacOS/cuckoo_generator "$@" ${INPUT_FILES}

    (I'm using Xcode 8.1 and cuckoo version 0.8.0) Any ideas? Thanks, Robert.

    opened by che1404 17
  • Runtime error with v1.3.0 and Swift 5.0

    Runtime error with v1.3.0 and Swift 5.0

    I am trying to use the latest version of library(1.3.0) since I am already using Swift 5.0, but I am getting the below error when I run the tests.

    Xcode Version: 11.3.0 Swift version: 5.0

    2020-01-27 16:35:42.999141+0530 mocker[45528:966664] Launching with XCTest injected. Preparing to run tests.
    2020-01-27 16:35:43.039552+0530 mocker[45528:966664] Failed to load test bundle from file://~/Library/Developer/Xcode/DerivedData/mocker-dycjgannhunhhibnccfljkqhsvtj/Build/Products/Debug-iphonesimulator/mocker.app/PlugIns/mockerTests.xctest/: Error Domain=NSCocoaErrorDomain Code=3587 "dlopen_preflight(~/Library/Developer/Xcode/DerivedData/mocker-dycjgannhunhhibnccfljkqhsvtj/Build/Products/Debug-iphonesimulator/mocker.app/PlugIns/mockerTests.xctest/mockerTests): Library not loaded: @rpath/libswiftCore.dylib
      Referenced from: ~/Library/Developer/Xcode/DerivedData/mocker-dycjgannhunhhibnccfljkqhsvtj/Build/Products/Debug-iphonesimulator/mocker.app/PlugIns/mockerTests.xctest/mockerTests
      Reason: image not found" UserInfo={NSLocalizedFailureReason=The bundle is damaged or missing necessary resources., NSLocalizedRecoverySuggestion=Try reinstalling the bundle., NSFilePath=~/Library/Developer/Xcode/DerivedData/mocker-dycjgannhunhhibnccfljkqhsvtj/Build/Products/Debug-iphonesimulator/mocker.app/PlugIns/mockerTests.xctest/mockerTests, NSDebugDescription=dlopen_preflight(~/Library/Developer/Xcode/DerivedData/mocker-dycjgannhunhhibnccfljkqhsvtj/Build/Products/Debug-iphonesimulator/mocker.app/PlugIns/mockerTests.xctest/mockerTests): Library not loaded: @rpath/libswiftCore.dylib
      Referenced from: ~/Library/Developer/Xcode/DerivedData/mocker-dycjgannhunhhibnccfljkqhsvtj/Build/Products/Debug-iphonesimulator/mocker.app/PlugIns/mockerTests.xctest/mockerTests
      Reason: image not found, NSBundlePath=~/Library/Developer/Xcode/DerivedData/mocker-dycjgannhunhhibnccfljkqhsvtj/Build/Products/Debug-iphonesimulator/mocker.app/PlugIns/mockerTests.xctest, NSLocalizedDescription=The bundle “mockerTests” couldn’t be loaded because it is damaged or missing necessary resources.}
    2020-01-27 16:35:43.039908+0530 mocker[45528:966664] Waiting to run tests until the app finishes launching.
    2020-01-27 16:35:43.207868+0530 mocker[45528:966664] The bundle “mockerTests” couldn’t be loaded because it is damaged or missing necessary resources. Try reinstalling the bundle.
    2020-01-27 16:35:43.207968+0530 mocker[45528:966664] (dlopen_preflight(~/Library/Developer/Xcode/DerivedData/mocker-dycjgannhunhhibnccfljkqhsvtj/Build/Products/Debug-iphonesimulator/mocker.app/PlugIns/mockerTests.xctest/mockerTests): Library not loaded: @rpath/libswiftCore.dylib
      Referenced from: ~/Library/Developer/Xcode/DerivedData/mocker-dycjgannhunhhibnccfljkqhsvtj/Build/Products/Debug-iphonesimulator/mocker.app/PlugIns/mockerTests.xctest/mockerTests
      Reason: image not found) 
    

    @TadeasKriz / Anyone else Please help me resolve this issue

    opened by vikytech 16
  • Many errors generating code with Xcode 9

    Many errors generating code with Xcode 9

    SWXMLHash.XMLIndexer:72:22: error: invalid redeclaration of 'Element'
    
    Carthage/Checkouts/Cuckoo/Generator/.build/checkouts/FileKit.git-2495357206000708207/Sources/FileSystemWatcher.swift:38:64: error: C function pointer signature '(ConstFSEventStreamRef, UnsafeMutableRawPointer?, Int, UnsafeMutableRawPointer, UnsafePointer<FSEventStreamEventFlags>?, UnsafePointer<FSEventStreamEventId>?) -> Void' (aka '(OpaquePointer, Optional<UnsafeMutableRawPointer>, Int, UnsafeMutableRawPointer, Optional<UnsafePointer<UInt32>>, Optional<UnsafePointer<UInt64>>) -> ()') is not compatible with expected type 'FSEventStreamCallback' (aka '@convention(c) (OpaquePointer, Optional<UnsafeMutableRawPointer>, Int, UnsafeMutableRawPointer, UnsafePointer<UInt32>, UnsafePointer<UInt64>) -> ()')
    
    error: terminated(1): /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-build-tool -f /Users/ben/Development/Projects/[redacted]/[redacted]/Carthage/Checkouts/Cuckoo/Generator/.build/debug.yaml main
    
    xargs: /Users/ben/Development/Projects/[redacted]/[redacted]/Carthage/Checkouts/Cuckoo/Generator/.build/debug/cuckoo_generator: No such file or directory
    

    Is this a known issue?

    opened by bsrz 16
  • Value of type Stubbing has no member...

    Value of type Stubbing has no member...

    I am trying to use Cuckoo with Carthage submodules but it is not working. GeneratedMock are generated correctly I think, but it does not recognize any method on the mock when I try stubbing.

    public protocol InteractorInputProtocol: class {
        weak var presenter: InteractorOutputProtocol! { get set }
    
        func fetchData()
    }
    
    public protocol InteractorOutputProtocol: class {
    }
    

    My test file:

    class ExampleTests: XCTestCase {
        private var mockInteractor: MockInteractorInputProtocol!
        private var sut: Presenter!
    
        override func setUp() {
            super.setUp()
    
            mockInteractor = MockInteractorInputProtocol()
    
            sut = Presenter(interactor: mockInteractor)
        }
        override func tearDown() {
            super.tearDown()
        }
    
        func test_viewDidLoad() {
            stub(mockInteractor) { stub in
                when(stub.fetchData()).thenDoNothing(_)
            }
            sut.viewDidLoad()
        }
    }
    

    As long as I see I did everything as it is written in documentation but still I got build error with message:

    "Value of type 'Stubbing' has no member 'fetchData' "

    You can reproduce the issue on my test project available under this url:

    https://github.com/zcsipler/ExampleProject.git

    To run carthage I use

    carthage update --use-submodules --no-build

    opened by zcsipler 15
  • Error when creating function Mocks with generics

    Error when creating function Mocks with generics

    Hello, I´m using Cuckoo for my test and I found a problem when you are generating the Stubs for functions that use generics.

    Let´s assume the following class

    class GenericWrapper<A> {
        
    }
    

    And then the following class for our generic function:

    class GenericFunc {
        
        func functionWithGenerics<U, Gen:GenericWrapper<U>>(arg: Gen) -> String {
            return "ABC"
        }
    }
    

    The problem comes when the function mock is created, the generated header is the following: override func functionWithGenerics<U, Gen: GenericWrapper<U>>>(arg: Gen) -> String

    As you can see the generic argument puts an extra '>' so the code doesn´t compile.

    bug PR submitted 
    opened by JJaviMS 14
  • Switch swift_version to 5.0 and add LIBRARY_SEARCH_PATHS in xcconfig

    Switch swift_version to 5.0 and add LIBRARY_SEARCH_PATHS in xcconfig

    This fixes running tests on iOS 10/11/12 with Cuckoo when tests are built using Xcode 11.

    It is a minimal required fix. I checked this, nothing can be removed from these changes to fix the issue.

    I think it is okay to switch to Swift 5 at the moment. It is the biggest change in terms of compatibility, it has stable ABI, etc. I was trying to keep my project in Swift 5, but I gave up after several days. You can make a tag named swift-4.2 for current HEAD in Cuckoo. People who use your project will still be able use your project.

    I think there is no point in holding on to the past now.

    opened by artyom-razinov 14
  • Compatibility with Carthage broken by OCMock inclusion via CocoaPods

    Compatibility with Carthage broken by OCMock inclusion via CocoaPods

    It seems that version 1.2.0 can no longer be integrated with Carthage due to the transitive dependency on OCMock, for the following reasons:

    • Cuckoo does not expose the transitive dependency in a Cartfile, so Carthage doesn't know that OCMock is required
    • Even if the Cartfile was added, building the Cuckoo+OCMock schemes would fail because the OCMock framework path specified in the project file is different from the path where Carthage places the built dependencies

    I'm not sure how to fix the second issue while keeping both CocoaPods and Carthage compatibility at the same time.

    bug 
    opened by dmazzoni 14
  • async/await is not fully supported in protocol with associated types

    async/await is not fully supported in protocol with associated types

    Although there is a merged PR here related to async/await #410, the feature is not fully supported, at least, in protocols with associated types.

    Example:

    protocol Datasource<T>{
        associatedtype T
        func get() async -> Result<T, Error>
    }
    

    Snippet from generated class:

    Screen Shot 2022-12-30 at 21 03 18

    IDE complains saying:

    Invalid conversion from 'async' function of type '() async -> Result<T, any Error>' to synchronous function type '() -> Result<T, any Error>'

    Solution: I have added required keywords to generated code, it is fixed:

    Screen Shot 2022-12-30 at 21 03 40
    opened by mecoFarid 5
  • Working with multiple Projects in one Workspace

    Working with multiple Projects in one Workspace

    Lets say I have three different projects in my workspace, first is DataLayer, containing only data related logic, DomainLayer, containing all the business logic and UILayer, which is responsible of UI presentation.

    Screenshot 2022-12-22 at 18 32 19

    To make this architecture works, some rules are:

    DataLayer can (or should) import DomainLayer DomainLayer should not have any dependencies UILayer should not know Interactor(DomainLayer). It should be behind of UseCase protocol (included in DomainLayer).

    My question is: I want to include cuckoo to test this project, but as I need to add some imports in generated files including mocks (UILayer implements protocols that are defined in Domain Layer and also DataLayer needs to include DomainLayer), is there a way to include this imports manually? Is it possible to use Cuckoo with this kind of setup?

    Thanks in advance!

    help wanted 
    opened by RamiroRamirez 1
  • Automatically add generated class to XCode project without manual drag/drop

    Automatically add generated class to XCode project without manual drag/drop

    In the CocoaPods documentations there is a part that says:

    # After running once, locate `GeneratedMocks.swift` and drag it into your Xcode test target group.

    Is there any way to automate this? Probably no one wants to manually play with generated code

    enhancement 
    opened by mecoFarid 4
  • [Suggestion] Swift Package Manager plugin

    [Suggestion] Swift Package Manager plugin

    Hello,

    First of all thanks for all the good work, guys. We've been using Cuckoo as a cocoapod for a long time now and we're currently in a process of trasitioning to use SPM as our only package manager.

    Is there currenty any option to run Cuckoo generator inside a SPM package? I've read the Readme, but the instructions for SPM installation describe steps needed to use the generator inside a XCode project, not a SPM package.

    The correct way to allow for generator usage inside a package would be to utilize SPM Plugins which are now supported in Xcode 14. A good example of that is SwiftGenPlugin. I thinkt the first step towards allowing the community to build such a plugin is to expose an artifact bundle through github.

    Is that currently possible for Cuckoo?

    Thanks.

    enhancement 
    opened by jzaczek 1
  • Method cannot be declared public because its parameter uses an internal type

    Method cannot be declared public because its parameter uses an internal type

    Since updating to the latest version, I am getting build errors in GeneratedMocks due to type accessibility. Property cannot be declared public because its type uses an internal type or Method cannot be declared public because its parameter uses an internal type

    For example:

    I have the following public class

    public class client {
        internal var delegates: [ClientDelegate]
    }
    
    class [ClientDelegate] {
    }
    

    However, cuckoo is trying to make the delegates var public:

    public override var delegates: [ClientDelegate] {
            get {
                return cuckoo_manager.getter("delegates",
                    superclassCall:
                        
                        super.delegates
                        ,
                    defaultCall: __defaultImplStub!.delegates)
            }
            
            set {
                cuckoo_manager.setter("delegates",
                    value: newValue,
                    superclassCall:
                        
                        super.delegates = newValue
                        ,
                    defaultCall: __defaultImplStub!.delegates = newValue)
            }
        }
    

    We do not want to make ClientDelegate a public class (this is a framework we are building) and we need the var to be internal so its value can easily be queried by tests.

    I see this has been mentioned in issue #292 and addressed in PR #293 But the issue came back. Is there a way to solve this?

    bug 
    opened by verebes1 1
Releases(1.9.1)
Owner
Brightify
It's not really about what, but how.
Brightify
A convenient mocking framework for Swift

// Mocking let bird = mock(Bird.self) // Stubbing given(bird.getName()).willReturn("Ryan") // Verification verify(bird.fly()).wasCalled() What is Mo

Bird Rides, Inc 545 Jan 5, 2023
A mocking framework for Swift

SwiftMock SwiftMock is a mocking framework for Swift 5.2. Notes on the history of this repo September 2015: first version of this framework November 2

Matthew Flint 257 Dec 14, 2022
Freezer is a library that allows your Swift tests to travel through time by mocking NSDate class.

Freezer Freezer is a library that allows your Swift tests to travel through time by mocking NSDate class. Usage Once Freezer.start() has been invoked,

Sergey Petrov 8 Sep 24, 2022
Efs - Easy function mocking/stubbing in Swift

Efs Efs, as the pronounced plural of letter F (for function), is a simple mockin

Jérémy Touzy 0 Feb 12, 2022
Mockingbird was designed to simplify software testing, by easily mocking any system using HTTP/HTTPS

Mockingbird Mockingbird was designed to simplify software testing, by easily mocking any system using HTTP/HTTPS, allowing a team to test and develop

FARFETCH 183 Dec 24, 2022
Library for unifying the approach to network mocking in iOS unit- & UI-tests.

TinkoffMockStrapping Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installati

Online financial ecosystem 22 Jan 3, 2023
A Matcher Framework for Swift and Objective-C

Nimble Use Nimble to express the expected outcomes of Swift or Objective-C expressions. Inspired by Cedar. // Swift expect(1 + 1).to(equal(2)) expect(

Quick 4.6k Dec 31, 2022
The Swift (and Objective-C) testing framework.

Quick is a behavior-driven development framework for Swift and Objective-C. Inspired by RSpec, Specta, and Ginkgo. // Swift import Quick import Nimbl

Quick 9.6k Dec 31, 2022
BDD Framework and test runner for Swift projects and playgrounds

Spectre Special Executive for Command-line Test Running and Execution. A behavior-driven development (BDD) framework and test runner for Swift project

Kyle Fuller 392 Jan 1, 2023
AutoMocker is a Swift framework that leverages the type system to let you easily create mocked instances of your data types.

AutoMocker Context AutoMocker is a Swift framework that leverages the type system to let you easily create mocked instances of your data types. Here's

Vincent Pradeilles 39 May 19, 2022
BDD-style framework for Swift

Sleipnir Sleipnir is a BDD-style framework for Swift. Sleipnir is highly inspired by Cedar. Also In Norse mythology, Sleipnir is Odin's steed, is the

Railsware 846 Nov 22, 2022
Swift Framework for TestRail's API

QuizTrain ?? ?? QuizTrain is a framework created at Venmo allowing you to interact with TestRail's API using Swift. It supports iOS, macOS, tvOS, and

Venmo 18 Mar 17, 2022
Swift framework containing a set of helpful XCTest extensions for writing UI automation tests

AutoMate • AppBuddy • Templates • ModelGenie AutoMate AutoMate is a Swift framework containing a set of helpful XCTest extensions for writing UI autom

PGS Software 274 Dec 30, 2022
A simple and lightweight matching library for XCTest framework.

Match A simple and lightweight matching library for XCTest framework. Getting started Swift Package Manager You can add Match to your project by addin

Michał Tynior 6 Oct 23, 2022
Genything is a framework for random testing of a program properties.

Genything is a framework for random testing of a program properties. It provides way to random data based on simple and complex types.

Just Eat Takeaway.com 25 Jun 13, 2022
A light-weight TDD / BDD framework for Objective-C & Cocoa

Specta A light-weight TDD / BDD framework for Objective-C. FEATURES An Objective-C RSpec-like BDD DSL Quick and easy set up Built on top of XCTest Exc

Specta / Expecta 2.3k Dec 20, 2022
Switchboard - easy and super light weight A/B testing for your mobile iPhone or android app. This mobile A/B testing framework allows you with minimal servers to run large amounts of mobile users.

Switchboard - easy A/B testing for your mobile app What it does Switchboard is a simple way to remote control your mobile application even after you'v

Keepsafe 287 Nov 19, 2022
Remote configuration and A/B Testing framework for iOS

MSActiveConfig v1.0.1 Remote configuration and A/B Testing framework for iOS. Documentation available online. MSActiveConfig at a glance One of the bi

Elevate 78 Jan 13, 2021
AB testing framework for iOS

ABKit Split Testing for Swift. ABKit is a library for implementing a simple Split Test that: Doesn't require an HTTP client written in Pure Swift Inst

Recruit Marketing Partners Co.,Ltd 113 Nov 11, 2022