What
    Swift Mocking Tools: To Use, or Not to Use, That Is the Question
    Why
    Compare Swift mocking tools like Cuckoo, SwiftyMocky, and SwiftMockGenerator for effective unit testing in iOS development.
    How
    Swift lacks dynamic runtime like Objective-C, making mocking more challenging. Cuckoo offers powerful verification but requires build script setup.
    Tech TrendsSwiftiOS

    Swift Mocking Tools: To Use, or Not to Use, That Is the Question

    Mocking in Swift isn't as straightforward as in Objective-C due to strict runtime safety. Fortunately, tools like Cuckoo, SwiftyMocky, and SwiftMockGenerator help generate test doubles for unit testing.

    Written by Sherif Adel
    Last Updated: 4/22/2024
    8 min
    Swift Mocking Tools: To Use, or Not to Use, That Is the Question
    TL;DR

    This deep dive compares Swift mocking frameworks including Cuckoo, Mockingbird, and SwiftyMocky. Protocol-based vs. class-based mocking strategies each have trade-offs for iOS development. Evolvice's nearshore iOS developers apply these patterns daily.

    Could you feel confident about the quality of your code without covering it with unit tests? The answer is no. However, unit testing often sounds scary for developers who have never tried it, especially in the Swift Mocking Tools. So, the very first step on the road of code testing, most certainly, would be searching for examples and tools that help avoid writing boilerplate code.

    As a savvy developer, you can follow the DRY principle and skip reinventing the wheel. Instead, select the library from the top ten search results with the enormous number of Github stars and, more importantly, a goosebump feeling—"Oh my precious."

    Everything would be smooth and easy, except that Swift is not Objective-C. Supplying test doubles is incredibly easy in Objective-C, where the central mechanism is messages. You could send any message to any object. If you had previous experience mocking Objective-C using OCMock or OCMockito, forget it.

    Swift was designed to be safe, supporting read-only reflection. Thus, there's no way to modify your program at runtime. Overall, it's good; the code is executed as expected, and other components cannot change it. But leading back to our topic, all mocking frameworks are built on reflection to be able to change classes, types, and objects at runtime.

    This language needs its superheroes to write testable code, and we know them well—protocols and extensions! No matter what implementation it is—class, enum, or struct—the purpose of protocols remains the same: to define abstractions and add new functionality to types, even ones we don't own.

    Overview

    For the demonstration purposes of each tool, let's create a simple protocol and class that adopts it with empty implementation.

    protocol AuthorizationServiceType {
        func login(email: String, password: String)
    }
    
    class AuthorizationService: AuthorizationServiceType {
        func login(email: String, password: String) {
            // implementation goes here
        }
    }

    Also, depending on this protocol, we need one more class. In tests, it will be our SUT.

    class LoginPresenter {
        private var authorizationService: AuthorizationServiceType
    
        init(authorizationService: AuthorizationServiceType) {
            self.authorizationService = authorizationService
        }
    
        func login(email: String, password: String) {
            authorizationService.login(email: email, password: password)
        }
    }

    We will examine how to write a test to verify the invocation of the function login with the correct input parameters using each tool.

    Years ago, one author said, "Swift is the Only Modern Language without Mocking Frameworks." Let's find out what has changed so far.

    Cuckoo

    One of the first mocking libraries for Swift appeared on GitHub in early 2016 and is still supported and updated. Also, it has the highest Github rating. The community is using it, so we give it a try.

    Install options

    Cocoapods, Carthage.

    Additional setup

    The Cuckoo installation process requires adding a new run script into your test target build phases. Include the absolute path to each file and the framework that will generate mocks in this script. Import the generated file into the project once, then re-generate it to keep it current. The class name starts with 'Mock' — you can use each test double as mock, stub, or spy.

    Mark protocol to mock

    Add an absolute file path into the script, like in the example:

    "${INPUT_DIR}/FileName.swift"

    Documentation

    The installation guide is complete; the number of usage examples is not large, but enough.

    Features

    Cuckoo's type matches cover basic value types, or you can extend any class by conforming to protocol Matchable yourself. Also, there are call and parameter matches to verify method invocation or return value. It supports generic protocols, classes, and methods. It's not so important, but there is even support for Objective-C.

    Live status

    The community is active, updates and fixes are pushed often, and everyone is invited to contribute.

    Test example

    func testLoginUsingCuckoo() {
        //Given
        let email = "test@gmail.com",
            password = "supersecure"
        let mock = MockAuthorizationService()
        let sut = LoginPresenter(authorizationService: mock)
        mock.enableDefaultImplementation(AuthorizationService())
        
        //In Cuckoo its required either provide default implementation or stub parameter/method
        //    stub(mock) { stub in
        //    when(stub.login(email: email, password: password)).thenCallRealImplementation()
        //    }
        
        //When
        sut.login(email: email, password: password)
        
        //Then
        verify(mock).login(email: email, password: password)
    }

    Please do not forget to add stub or default implementation, otherwise the test will fail.

    Summary

    This library is simple and greatly helps with mocking. Although adding classes to be mocked is inconvenient, being a con simultaneously is a big pro of Cuckoo since there is a very low dependency on your code, and all work can be done inside the test target. Generated code is put into one file that grows tremendously with your project. The only reason to edit it could be an import of external frameworks.

    SwiftyMocky

    Lightweight framework for mocking with an idea to mock Swift protocols automatically. It depends on the source used for scanning code and generating mocks.

    Install options

    • Cocoapods
    • Carthage
    • SPM

    Additional setup

    SwiftyMocky has a 2-step installation. First, set up SwiftyMocky CLI on your working machine. Second, add it to the project via one of the three options listed above. Open Terminal in your project root folder and execute the following commands one by one:

    > swiftymocky setup   # if you don't have a Mockfile yet
    > swiftymocky doctor  # validate your setup
    > swiftymocky generate # generate mocks

    The result of the setup command is a Mockfile, which can be edited for advanced settings. Use the command doctor to verify the setup status before generating. On the first command call, the generated file should be imported into a project. Then, each time, mock generation will update the file. Every generated test double can be used as a mock or stub.

    Mark Protocol to Mock

    Option 1: Add conformance to the protocol you want to mock by AutoMockable. Option 2: Before your protocol, add annotation //sourcery: AutoMockable.

    Documentation

    The installation guide is complete, and the usage documentation is great and covers many cases.

    Features

    The same as in Cuckoo: matches, wrapped parameters, and the option to act as a stub call. Matching custom classes requires registration and implementation of the Equatable protocol.

    Live Status

    Based on commits, two guys are doing the real thing, updates and fixes are pushed once a month, and there are few good tutorials on library usage.

    Test Example

    func testLoginUsingSwiftyMocky() {
        //Given
        let email = "test@gmail.com",
            password = "supersecure"
        let mock = AuthorizationServiceTypeMock()
        let sut = LoginPresenter(authorizationService: mock)
    
        //When
        sut.login(email: email, password: password)
    
        //Then
        Verify(mock, .login(email: .value(email), password: .value(password)))
    }

    Summary

    You always have a choice of how to install lib and by which tool to generate mocks, and you can do it manually or automatically. Even though additional dependency on Sourcery and the 2-step installation process might look too much, that's for the standalone generation process. Usage of this library in an actual project is easy.

    SwiftMockGenerator

    Spoiler! It's not a Library but an Xcode plugin that helps solve the same problem—generating test doubles. SwiftMockGeneratorForXcode is an exception in this list of frameworks. Instead of being integrated into the project, this generator can only be added as an extension to Xcode. Also, you won't have custom verifiers.

    Spoiler! It's not a Library but an Xcode plugin that helps solve the same problem—generating test doubles. SwiftMockGeneratorForXcode is an exception in this list of frameworks. Instead of being integrated into the project, this generator can only be added as an extension to Xcode. Also, you won't have custom verifiers or matches; do all assertions using the XCTest framework.

    Install options

    The installation process is the same as for any program on Mac—drag to Applications and allow it to control Xcode in system preferences.

    Additional setup

    No additional integration into the project is required.

    Mark protocol to mock

    Create an empty class that will be your mock implementation, make an inheritance to protocols or classes that should be mocked, and put the cursor into the class declaration. From there, go to Xcode Editor -> Mock Generator, selecting one of 4 available options: spy, stub, dummy, or partial spy.

    Documentation

    The GitHub page contains a fully described guide on installing a set of examples with explanations.

    Features

    It supports Swift 5, can mock classes or protocols, and handles closures, throws, and failable initializers. However, it has limitations, like supporting properties with inferred type; generics support is not yet done. Also, generating test doubles could be bound to keyboard shortcuts.

    Live status

    When writing the article, this plugin looked like one of the most frequently updated. It already works on the newly launched Xcode 11, and the author is open to new feature requests.

    Test example

    func testLoginUsingSwiftMockGenerator() {
        //Given
        let email = "test@gmail.com",
            password = "supersecure"
        let mock = SpyAuthorizationService()
        let sut = LoginPresenter(authorizationService: mock)
    
        //When
        sut.login(email: email, password: password)
    
        //Then
        XCTAssertTrue(mock.invokedLogin)
        guard let parameters = mock.invokedLoginParameters else {
            XCTFail("No parameters")
            return
        }
        XCTAssertTrue(parameters == (email: email, password: password), "Wrong parameters")
    }

    Summary

    This solution has tremendous advantages—no coupling between code and tests at all, a good variety of supported test doubles, and it seems like the project won't have current results and will move forward. The disadvantages worth noting are that there is no support for generics, and sometimes, the generation process is slow.

    Conclusion

    We, as developers, do not have many tools for mocking on Swift, and there are some strict boundaries due to language-limited runtime access. So it's essential to mention one more obvious option—writing test doubles independently, without any tools. Here, we come to the critical question of whether or not to use external frameworks for mocking.

    Well-known Uncle Bob recommends not using them as much as possible: "The point at which you start to need a mocking framework is the point at which the coupling between your tests and code is getting too high. After that, however, you should strive to keep the coupling between your code and tests low enough that you don't need to use the mocking framework very often."

    In Evolvice, we also have different thoughts and personal preferences about using Swift Mocking Tools or not. We believe there is no wrong choice if it leads to code improvement and tests quality, stability, and reliability—that's why. Of course, having options is a little good, but the final decision is yours.

    Meet the Team

    Sherif Adel

    Sherif Adel

    Author

    Amira Adel

    Amira Adel

    Editor

    Need iOS development expertise?

    Hire Swift Developers

    Frequently Asked Questions

    Why is unit testing important in Swift development?

    Unit testing ensures your code behaves as expected and helps you catch bugs early. In Swift, due to limited runtime reflection, writing tests requires more planning, especially when mocking dependencies.

    What makes mocking in Swift harder than in Objective-C?

    Swift was designed with safety in mind and lacks the dynamic runtime capabilities of Objective-C. This means mocking via reflection (common in Objective-C) isn't possible in Swift, requiring tools like Cuckoo or SwiftyMocky to generate test doubles.

    What is the best mocking tool for Swift?

    There is no single best tool—it depends on your project needs. Cuckoo is powerful but requires setup, SwiftyMocky is flexible with CLI support, and SwiftMockGenerator is easiest for beginners as an Xcode plugin.

    Do Swift mocking tools support generics?

    Tools like Cuckoo and SwiftyMocky offer limited support for generics. SwiftMockGenerator currently does not support generics.

    Is it okay to write mock objects manually instead of using a tool?

    Yes, writing test doubles manually is a valid and often recommended approach. Even Uncle Bob advises minimizing mocking framework usage to keep tests loosely coupled and maintainable.

    Which mocking tool has the easiest setup for beginners?

    SwiftMockGenerator is the easiest to set up, as it's just a plugin for Xcode. It requires no integration into your project, making it ideal for beginners.

    Can I use mocking tools with protocols, classes, and enums?

    Yes. Swift mocking tools primarily work with protocols. Cuckoo and SwiftyMocky support mocking protocols and classes. Enums are generally not mockable unless abstracted behind protocols.

    How often should mocks be regenerated?

    Whenever the underlying protocol or class changes, you should regenerate your mocks to keep them in sync with the codebase.

    Are there any downsides to using Swift mocking libraries?

    Yes. These tools may introduce overhead in setup, additional dependencies, and can slow down compile times or test builds. Choosing the right tool depends on your team's workflow and project needs.

    Related Posts

    Recommended Expertise

    Explore related topics from the Evolvice knowledge base