Framework for mock generation. Adds a set of handy methods, simplifying testing.
Clone or download
Latest commit b828371 Oct 25, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Mocky.xcodeproj Move `Count` struct to separate file. Oct 24, 2018
Mocky.xcworkspace/xcshareddata Updated pod file lock and targets, regenerated mocks May 2, 2018
Sources Typo fix. Oct 24, 2018
SwiftyMocky-Example Added: allow no stub defined for methods, properties and subscripts s… Oct 2, 2018
SwiftyMocky-Runtime Added mocky setup on load Sep 30, 2018
SwiftyMocky-Tests Typo fix. Oct 24, 2018
certs fix: setup signing settings, removed info.plist from copy phase Nov 27, 2017
docs Updated docs Oct 2, 2018
fastlane feat: added macOS support Nov 27, 2017
guides Updated docs Oct 2, 2018
.gitignore Fixed issue with generics not recognized as generics Apr 4, 2018
.jazzy.yaml Regenerated docs, added descriptions in code, refreshed some descript… Sep 30, 2018
.mocky.iOS.yml doc: added exclude documentation case Jan 8, 2018
.mocky.macOS.yml doc: added exclude documentation case Jan 8, 2018
.mocky.tvOS.yml doc: added exclude documentation case Jan 8, 2018
.swift-version add default swift version Aug 28, 2017
.travis.yml Updated podfile.lock and rakefile Sep 26, 2018
CODE_OF_CONDUCT.md Create CODE_OF_CONDUCT.md Nov 21, 2017
CONTRIBUTING.md doc: updated contribute guide Jan 9, 2018
LICENSE swifty mocky add default pod template May 19, 2017
Podfile Make parameter expressible by literals, cleanups, version bump Sep 26, 2018
Podfile.lock Minor updates for Custom subspec Sep 30, 2018
README.md Updated docs Oct 2, 2018
Rakefile Make parameter expressible by literals, cleanups, version bump Sep 26, 2018
SwiftyMocky.podspec Minor updates for Custom subspec Sep 30, 2018
certs.sh fix: setup signing settings, removed info.plist from copy phase Nov 27, 2017
get_sourcery.sh Updated documentation, pod spec, version bump to 2.1.0 Sep 26, 2018
icon.png feat: examples section Nov 17, 2017
mock-regen.sh Added support for static, support for generics, simplify template (#50) Nov 14, 2017
mock.sh Added support for static, support for generics, simplify template (#50) Nov 14, 2017

README.md

behaviourBuild Status Docs License Platform Version Carthage compatible

logo SwiftyMocky

Join our community on Slack! -> invitation link here

Check out guides, or full documentation

Overview

SwiftyMocky is Lightweight, strongly typed framework for Mockito-like unit testing experience. As Swift doesn't support reflections well enough to allow building mocks in runtime, library depends on Sourcery, that scans your source code and generates Swift code for you.

The idea of SwiftyMocky is to mock Swift protocols. The main features are:

  • easy syntax, utilising full power of auto-complete, which makes writing test easier and faster
  • we DO support generics
  • mock implementations generation
  • a way to specify what mock will return (given)
  • possibility to specify different return values for different attributes
  • record stubbed return values sequence
  • verify, whether a method was called on mock or not
  • check method invocations with specified attributes
  • it works with real device

Installation

SwiftyMocky is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "SwiftyMocky"

Then add mocky.yml and Rakefile (or build script phase) to your project root directory, as described in below. See full setup instructions.

For Carthage install instructions, see full documentation.

Generating mocks implementations:

One of the boilerplate part of testing and development is writing and updating mocks accordingly to newest changes. SwiftyMocky is capable of generating mock implementations (with configurable behavior), based on protocol definition.

During development process it is possible to use SwiftyMocky in a watcher mode, which will observe changes in you project files, and regenerate files on the fly.

Generating mock

By default, all protocols marked as AutoMockable (inheriting from dummy protocol AutoMockable or annotated with //sourcery: AutoMockable) are being subject of mock implementation generation. All mocks goes to Mock.generated.swift, which can be imported into test target.

How to start using SwiftyMocky

Mocks generation is based on mocky.yml file.

First, create file in your project root directory with following structure:

sources:
  include:
    - ./ExampleApp
    - ./ExampleAppTests
templates:
  - ./Pods/SwiftyMocky/Sources/Templates
output:
  ./ExampleApp
args:
  testable:
    - ExampleApp
  import:
    - RxSwift
    - RxBlocking
  excludedSwiftLintRules:
    - force_cast
    - function_body_length
    - line_length
    - vertical_whitespace
  • sources: all directories you want to scan for protocols/files marked to be auto mocked, or inline mocked. You can use exclude, to prevent from scanning particular files (makes sense for big *.generated.swift files)
  • templates: path to SwiftyMocky sourcery templates, in Pods directory
  • output: place where Mock.generated.swift will be placed
  • testable: specify imports for Mock.generated, that should be marked as @testable (usually tested app module)
  • import: all additional imports, external libraries etc. to be placed in Mock.generated
  • excludedSwiftLintRules: if using swift SwiftLint.

Generate mocks:

  1. manually: by triggering:

Pods/Sourcery/bin/sourcery --config mocky.yml

  1. in watch mode: changed methods will be reflected in mocks, after generation of mock, by triggering:

Pods/Sourcery/bin/sourcery --config mocky.yml --watch

!!! In case of incompatibile swift module versions error - check Known issues section in guides or docs

Don't forget to add Mock.generated.swift to your test target :)

Please Note! Most convenient way is to put generation in some kind of script - like Rakefile below. Just create file named Rakefile - generation is triggered by rake mock

# Rakefile
task :mock do
  sh "Pods/Sourcery/bin/sourcery --config mocky.yml"
end

task :mock_watcher do
  sh "Pods/Sourcery/bin/sourcery --config mocky.yml --watch"
end

Marking protocols to be mocked

Mark protocols that are meant to be mocked with sourcery annotation as following:

//sourcery: AutoMockable
protocol ToBeMocked {
  // ...
}

Every protocol in source directories, having this annotation, will be added to Mock.generated.swift

@objc protocols are also supported, but needs to be explicitly marked with ObjcProtocol annotation:

//sourcery: AutoMockable
//sourcery: ObjcProtocol
@objc protocol NonSwiftProtocol {
  // ...
}

Stubbing return values for mock methods - Given

All mocks has given method (accessible both as instance method or global function), with easy to use syntax, allowing to specify what should be return values for given methods (based on specified attributes).

Generating mock

All protocol methods are nicely put into Given, with matching signature. That allows to use auto-complete (just type .) to see all mocked protocol methods, and specify return value for them.

All method attributes are wrapped as Parameter enum, allowing to choose between any and value, giving great flexibility to mock behaviour. Please consider following:

Given(mock, .surname(for name: .value("Johnny"), willReturn: "Bravo"))
Given(mock, .surname(for name: .any, willReturn: "Kowalsky"))

print(mock.surname(for: "Johny"))   // Bravo
print(mock.surname(for: "Mathew"))  // Kowalsky
print(mock.surname(for: "Joanna"))  // Kowalsky

In verions 3.0 we introduced sequences and policies for better control of mock behvaiour.

Given(mock, .surname(for name: .any, willReturn: "Bravo", "Kowalsky", "Nguyen"))

print(mock.surname(for: "Johny"))   // Bravo
print(mock.surname(for: "Johny"))   // Kowalsky
print(mock.surname(for: "Johny"))   // Nguyen
print(mock.surname(for: "Johny"))   // and again Bravo
// ...

For more details please see full documentation.

Check invocations of methods, subscripts and properties - Verify

All mocks has verify method (accessible both as instance method or global function), with easy to use syntax, allowing to verify, whether a method was called on mock, and how many times. It also provides convenient way to specify, whether method attributes matters (and which ones).

Generating mock

All protocol methods are nicely put into Verify, with matching signature. That allows to use auto-complete (just type .) to see all mocked protocol methods, and specify which one we want to verify.

All method attributes are wrapped as Parameter enum, allowing to choose between any, value and matching, giving great flexibility to tests. Please consider following:

// inject mock to sut. Every time sut saves user data, it should trigger storage storeUser method
sut.usersStorage = mockStorage
sut.saveUser(name: "Johny", surname: "Bravo")
sut.saveUser(name: "Johny", surname: "Cage")
sut.saveUser(name: "Jon", surname: "Snow")

// check if Jon Snow was stored at least one time
Verify(mockStorage, .storeUser(name: .value("Jon"), surname: .value("Snow")))
// storeUser method should be triggered 3 times in total, regardless of attributes values
Verify(mockStorage, 3, .storeUser(name: .any, surname: .any))
// storeUser method should be triggered 2 times with name Johny
Verify(mockStorage, 2, .storeUser(name: .value("Johny"), surname: .any))
// storeUser method should be triggered at least 2 times with name longer than 3
Verify(mockStorage, .moreOrEqual(to: 2), .storeUser(name: .matching({ $0.count > 3 }}), surname: .any))

For Verify, you can use Count to specify how many times you expect something to be triggered. Count can be defined as explicit value, like 1,2,... or in more descriptive and flexible way, like .never, more(than: 1), etc.

From SwiftyMocky 3.0, it is possible to use Given and perform Verify on properties as well, with respect to whether it is get or set:

mock.name = "Danny"
mock.name = "Joanna"

print(mock.name)

// Verify getter:
Verify(mock, 1, .name)
// Verify setter:
Verify(mock, 2, .name(set: .any))
Verify(mock, 1, .name(set: .value("Danny")))
Verify(mock, .never, .name(set: .value("Bishop")))

The old VerifyProperty is now deprecated. We also deprecated using setters for readonly properties, in favour of using Given.

All supported features

For list all supported features, check documentation here or guides

Example of usage

For more examples, check out our example project, or examples section in guides.

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

To trigger mocks generation, run rake mock from root directory. For watcher mode, when mocks are generated every time you change your file projects, use rake mock_watcher instead.

Documentation

Full documentation is available here, as well as through docs directory.

Guides - Table of contents

Changelog is available here

Roadmap

  • stubbing protocols in elegant way
  • template for generating mocks
  • example project
  • stubbing protocols with variables
  • method signature generation without name conflicts
  • cover 95% of framework codebase with unit tests
  • cover 95% of framework codebase with documentation
  • add unit tests for template
  • support for tvOS, Linux and MacOS
  • Carthage support
  • Subscripts support
  • Stub return values as sequences
  • Simple tool simplifying configuration process

Current version

As we value stability, there should be no breaking changes in version 3.0.0. Nevertheless, we explicitly marked some parts as deprecated, as they will be removed in version 3.1.x. The main reason is because we want to simplify and unify mocking experience.

Authors

License

SwiftyMocky is available under the MIT license. See the LICENSE file for more info.