⚠️ This plugin is no longer being actively worked on as the author is no longer writing much Swift. I will try to keep the app working but won't be adding new features. Thank you everyone for your support!
❗️ Your app may stop working after 10th September 2022 as the signing certificate will expire. Download and install the most recent version to fix this issue.
An Xcode extension (plugin) and command line tool to generate spy, stub, dummy, and partial spy classes automatically.
Looking for the AppCode version?
- Close Xcode if it is open
- Download the latest release here
- Copy the app to the
Applicationsfolder. - Open the app
- Select OK for '"Swift Mock Generator for Xcode" wants access to control "Xcode"'
- Go to
System Preferences -> Extensions -> Xcode Source Editorand make sureMock Generatoris enabled. - Go to
System Preferences -> Security & Privacy -> Privacy -> Automationand make sureSwift Mock Generatoris enabled. - Open Xcode
This extension is fully sandboxed which means you need to give permission to read your project files before using it.
- Open the companion app.
- Press "Give permission to read directory".
- Select the directory and press "Grant permission".
- In Xcode, generate your test double.
- Open the companion app.
- Press the select directory button.
- Select the directory and press "Open".
- In Xcode, generate your test double.
Please note if using manual project paths before v0.25 you will have to select your project path again.
- Create an empty class inheriting from a class or protocols that you wish to mock.
Example:
class MyProtocolSpy: MyProtocol {
}
- Place the cursor inside the class declaration.
- Click
Editor -> Mock Generator -> Generate spy.
There's no need to delete the old code. Simply place the cursor anywhere inside the class declaration and generate the test double as before.
- Select preferences
⌘,in Xcode. - Choose 'Key Bindings'.
- Search for 'Mock Generator'.
- Choose your preferred shortcut.
The mock generator will replace anything that is currently in your class with the generated test double.
Undo is supported for Xcode plugins but you're safer to use a version control system such as git in the event of unexpectedly generating a test double.
For convenience, create a symbolic link to the CLI.
$ ln -s "/Applications/Swift Mock Generator for Xcode.app/Contents/MacOS/genmock" /usr/local/bin/genmock
Use $ genmock --help for a list of options.
See how this project generates its mocks here.
* properties with inferred types are not supported
** generic arguments in closures and generic types are not supported, generic subscript parameters are also not supported
If there is a feature you need, check for an existing GitHub issue and make a comment or, if no issue exists, raise a new issue.
A protocol called Animator that we wish to spy on:
protocol Animator {
func animate(duration: TimeInterval, animations: () -> (), completion: (Bool) -> ()) -> Bool
}
Create a spy class conforming to a protocol:
class AnimatorSpy: Animator {
}
Generate the spy:
class AnimatorSpy: Animator {
var invokedAnimate = false
var invokedAnimateCount = 0
var invokedAnimateParameters: (duration: TimeInterval, Void)?
var invokedAnimateParametersList = [(duration: TimeInterval, Void)]()
var shouldInvokeAnimateAnimations = false
var stubbedAnimateCompletionResult: (Bool, Void)?
var stubbedAnimateResult: Bool! = false
func animate(duration: TimeInterval, animations: () -> (), completion: (Bool) -> ()) -> Bool {
invokedAnimate = true
invokedAnimateCount += 1
invokedAnimateParameters = (duration, ())
invokedAnimateParametersList.append((duration, ()))
if shouldInvokeAnimateAnimations {
animations()
}
if let result = stubbedAnimateCompletionResult {
completion(result.0)
}
return stubbedAnimateResult
}
}
Inject the spy into the class you wish to test:
let animatorSpy = AnimatorSpy()
let object = ObjectToTest(animator: animatorSpy)
Test if animate method was invoked:
func test_spyCanVerifyInvokedMethod() {
object.myMethod()
XCTAssertTrue(animatorSpy.invokedAnimate)
}
Test the correct parameter was passed to the animate method:
func test_spyCanVerifyInvokedParameters() {
object.myMethod()
XCTAssertEqual(animatorSpy.invokedAnimateParameters?.duration, 5)
}
Test the number of times animate was invoked:
func test_spyCanVerifyInvokedMethodCount() {
object.myMethod()
object.myMethod()
XCTAssertEqual(animatorSpy.invokedAnimateCount, 2)
}
Test the parameters passed into each call of the animate method:
func test_spyCanVerifyMultipleInvokedMethodParameters() {
object.myMethod()
object.myMethod()
XCTAssertEqual(animatorSpy.invokedAnimateParametersList[0].duration, 5)
XCTAssertEqual(animatorSpy.invokedAnimateParametersList[1].duration, 5)
}
Stub a return value for the animate method:
func test_spyCanReturnAStubbedValue() {
animatorSpy.stubbedAnimateResult = true
let result = object.myMethod()
XCTAssertTrue(result)
}
Stub the value for the completion closure in the animate method:
func test_spyCanCallClosure_withStubbedValue() {
animatorSpy.stubbedAnimateCompletionResult = (false, ())
object.myMethod()
XCTAssertFalse(object.animationDidComplete)
}
Spies are stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent. See reference
Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. See reference
Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists. See reference
Partial spies are spies which can also forward calls to the original implementation.
To disable:
Go to System Preferences -> Extensions and deselect the extension under Xcode Source Editor.
To remove:
Delete the app.
You might find that you are not able to see the plugin in the Editor menu or even in the Extensions preference pane. This seems to be caused by installing your Xcode version(s) outside of the App Store. If this happens, open Terminal and run:
$ pluginkit -m -p com.apple.dt.Xcode.extension.source-editor -A -D -vvv
If you can't see the plugin in this list then you can reset your list of plugins by running the following in Terminal:
$ PATH=/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support:"$PATH"
$ lsregister -f /Applications/Xcode.app
See here for more information.
If the above fails, then completely removing the plugin and downloading/installing it again seems to work.
Despite being called a Mock Generator, this plugin actually generates a spy, stub or dummy. The word 'mock', whilst not technically correct, has been used because test doubles such as spies, mocks, and stubs have become commonly known as mocks.
Many thanks to the contributors of kotlin-native for a Kotlin LLVM backend which enables code sharing between the Xcode and AppCode plugins.
Many thanks to the contributors of GRMustache for a enabling great Mustache test double templates.
Many thanks to the contributors of Commander for enabling an easy-to-use CLI.
And special thanks to everyone who is contributing by raising issues and feature requests.
This tool wouldn't exist without you all!

