- Writing a protocol for the class or struct to be mocked
- Injecting that protocol as a dependency
- Writing a mock class conforming to that protocol
- And finally, injecting an instance of the mock into the class or struct being tested.
These steps are then duplicated many times because many classes and structs have multiple dependencies that each require their own mocks. Clearly, stubbing out functionality in Swift takes quite a few more steps compared to
Some other issues also present themselves quickly once you start modifying others’ projects with unit tests and even just modifying your own tests and code.
- Multiple contributors in a code base means mock classes could have inconsistent manual implementations. Some will have a Boolean
myFunctionCalledvariable, while others will have an Integer
myFunctionCallCountvariable; one could use a tuple
lastParametersCalledWithvs. an array of tuples,
myFunctionCalledWith. Without checks or automation, developers don’t always hold themselves or others accountable to their own rules, patterns, or standards that help speed up development.
- Any changes to a protocol means fixing compile errors for their mocks.
- Poorly written mocks could be missing expected properties of mocks completely
There Has to Be a Better Way
Parrot started out as two separate attempts at the same problem which ended up not overlapping at all. I had written a rough script to find the first protocol definition in a given file and psuedo-parse the code in order to generate a mock. It was a bare-bones Swift script (I personally really like scripting with Swift). A friend in my group of developers had started writing code that would scan a directory recursively and identify the Swift files. It was exciting realizing what we could make together. We took a couple days putting the pieces together, and we had our first pass at a Swift mock generator. The initial implementation definitely had some glaring holes in it, and it certainly isn’t “finished” today. Even so, the benefits are evident. Generating your mocks with Parrot guarantees they will all be written in the same format, with the same interface to assert against in tests, and there won’t be any missing implementations. It’s meant to generate before the compile time of your tests, so any protocol changes in the project will be reflected in your mock definitions automatically.
See How It Works
Adding Parrot to a Project
This and more details about usage can be found in the Parrot README.
When we started Parrot, there were not similar tools to be found and the ones that did exist had taken directions we didn’t want to follow. At the time of writing this post, another option exists: Swift Mock Generator for Xcode. It’s used as an Xcode extension. Readers should feel free to compare and contrast these two tools.
Parrot is far from perfect and has room for enhancements. From what I’ve seen, the majority of generated mocks work right out of the box while the ones that don’t only need a small amount of modification. I hope Parrot makes Swift testing a much better experience for all who use it, and I’m looking forward to its future enhancement. https://github.com/MonsantoCo/Parrot