Using SwiftyMocky to generate mocks and simplify unit testing in Swift

There are a lot going on lately in Swift world in general, and the trend did not miss testing part of development. While good testing practices are spreading, the need for tools designed to aid that process also grows. I have a pleasure to be a part of a team working on one of the solutions called SwiftyMocky.

1. Mock is Test Double

When talking of unit testing and testing in general, there is a whole set of special purpose objects, called Test Doubles. In this article I will only briefly go through them, as it is needed to understand whole concept behind SwiftyMocky.

The whole story behind Test Doubles is quite simple. The, for lack of better word, classic way of testing often relies on state verification. We create initial state for sut and its dependencies, and then verify, whether state after performing test matches our expectations. Still, not in every test case we are testing sut, we can use real implementations as its dependencies.

Good example are database operations, or network calls. We don’t really want to make real calls there, the knowledge that sut tried to do them is usually enough to assure validity. Another good example (from Martin Fowler post) is sending email — hard to verify from test perspective.

That’s where our special case objects come into picture. Based on the vocabulary proposed by Gerard Meszaros (which i personally found pretty useful) I would split them into following categories:

Dummy: no implementation at all, the whole and sole purpose of that object is to satisfy dependency requirement.

Fake: simple yet working implementation, usually taking shortcuts. Fake storage will for example rather use some in-memory data structure like array or dictionary, rather than wrap up core data operations. While offering same features as concrete implementation, it enables easier state verification.

For the rest of article we will focus on Mock, as it handles most of the stuff needed for proper testing.

Please note, that while vocabulary above strictly distinct between test doubles types, in most tools Mock is extended, with combined functionality of Stub and Spy. In SwiftyMocky we do the same, so whenever we refer to Mock, we mean object that also provides both of Stub and Spy features.

While idea of having Mock objects is quite clear, and benefits cannot be overstated, that leaves us with one problem. The mock implementations have to be written, which can become quite an overhead in big projects, with proper layer separation.

In languages that have proper reflections, there are numbers of libraries and frameworks that can create Mock in runtime, allowing you to choose one, fitting best your style of testing.

2. SwiftyMocky — the idea

As Swift does not support reflections (yet?), other approaches are needed. There are bunch of solutions that are partially manual (when you have to implement part of the mock manual), and a few that uses meta-programming to generate complete mock implementation.

SwiftyMocky falls into second category (originally proposed by Przemysław Wośko). Whole concept is based on Sourcery (written by Krzysztof Zabłocki) and utilizes meta-programming concept.

First step is scanning sources, and checking for types, that could be subject of mocking. In SwiftyMocky it is done by either annotations, or adopting AutoMockable protocol.

Second step is generation of swift file, containing all mock’s implementations, adopting from variety of Mock protocols (like Mock and StaticMock to name few). That file can be added to test target, allowing to use all generated classes.

In step three we could write tests, using whole set of handy methods for classes adopting Mock protocols, allowing:

Please note, that for now only protocols are subject of Mock generation. We believe that interfacing is the proper way of layer separation and setting up dependencies in Swift. However, mocking classes are possible in the future (but not without limitations)

Easy stubbing:

One of the most important things for our mock implementation, if it’s gonna be anything more than dummy, is a way to specify method return values. In typical plain stub we just hardcode value, but its not very flexible.

// For all calls with name Johny, we should return Bravo Given(mock, .surname(for: .value("Johny"), willReturn: "Bravo")) // For all other calls, regardless of value, we return Kowalsky Given(mock, .surname(for: .any, willReturn: "Kowalsky"))

Please note . at the beginning of the method, as it strictly refers to:

Autocomplete:

We utilize as much power of autocomplete as possible, to aid process of writing tests. When performing Given, Verify, Perform on mock, type . to get list of all methods declared by mocked protocol, that fits particular case. All of that is type-safe.

That is the situation, where one image is worth thousand words:

We don’t force user into remembering additional stuff. And there is no longer a need for error prone “String” identifiers (like OCMock does).

Easy spying:

Stubbing is often not enough, and so we prepared a way to verify, whether a method was called (and how many times). Syntax is consistent with the one proposed in Given:

Wherever it makes sense, we wrap method attributes into Parameter enum. It allows to specify if we care about explicit value, or not:

using .any is wildcard, so we can specify return values regardless of attributes, or verify if the method was called at all.

using .value() we can specify explicit value. As more explicit cases takes precedence over less explicit ones, we can specify special cases for return values, or verify exact numbers of method being called with specific attribute.

All the cases could be mixed together, giving quite nice flexibility for stubbing and spying.

In the moment I’m writing that, version 2.0 is on its way, adding .matching(Type -> Bool) case to Parameter, providing even more flexibility.

Generics:

When I’m writing that, it seems that SwiftyMocky is the only Swift tool that supports generics. (Or at least both generics and mock generation).

4. Installation and Setup

SwiftyMocky is available both by CocoaPods and Carthage, and additional steps required to run it differs a bit, based on dependency manager used. Full instructions are available here.

The simplest approach would be to use CocoaPods, as it handles getting Sourcery as well (in case of carthage you would have to do it yourself).

While I’m writing that, Sourcery is at version 0.9.0, compiled for Swift 4.0.0. It often leads to problems for users of Xcode 9.1, so along with SwiftyMocky comes script to override Sourcery in such a cases (check here)

Whole setup is done in yml config file. We could specify multiple locations for source files, both as list of files or folders. It usually looks something like below:

As mocks generation takes some time, we see no value in adding it to build run script phases, but we found it quite convenient to use Xcode behaviors and key bindings to regenerate mocks in project.

5. Summary

While there were several good tools for obj-c, Swift was still lacking some proper solutions. SwiftyMocky tries to address that, providing automatic mock generation and easy to get and use syntax, utilizing as much power of auto-complete as possible.

It is still a new thing, getting more mature overtime, yet bigger changes are still possible. Good thing about that is it still have velocity required to grow, providing new features and fixing current.