# Mockolo: generating Swift mocks fast enough to run in your build

> Mockolo is a command line mock generator for Swift that parses your source with SwiftSyntax and writes mock implementations of your protocols, and its whole argument for existing is speed: on a codebase of over 2 million lines and more than ten thousand protocols, other tools take hours and even cached ones take minutes, while Mockolo is built to finish in seconds.

**uber/mockolo** — Efficient Mock Generator for Swift

- Repository: https://github.com/uber/mockolo
- Stars: 886 · Forks: 100
- Language: Swift
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/uber-mockolo

## The argument is seconds, not minutes

The motivation section is the most informative paragraph in the README, because it names the specific problem rather than the general one.

One of the main objectives of this project is high performance. There aren't many third party tools that perform fast on a large codebase containing, for example, over 2M LoC or over 10K protocols. They take several hours and even with caching enabled take several minutes. Mockolo was built to make highly performant generation of mocks possible, in the magnitude of seconds, on such a codebase. It uses a minimal set of frameworks necessary to keep the code lean and efficient.

That is a narrow but real claim, and it reframes the tool. Mockolo is not competing on features, it is competing on the size of the job you can put in a CI step. A tool that takes three minutes is a nightly job. A tool that takes three seconds is something you can run on every build, or in a pre-commit hook, or as part of a linter.

The second objective is about types. Another objective is to enable flexibility in using or overriding types if needed, and the documentation says this allows use of some of the features that require deeper analysis, such as protocols with associated types, to be simpler, more straightforward and less fragile.

That is the second reason to look at it. A mock generator that cannot handle a protocol with an associated type is going to be unusable on a modern codebase, and a generator that lets you override how a particular type is represented is a generator you can live with.

The topic list on the project record names the tools it leans on, swift and swiftsyntax, and the categories it is aiming at, mocking-framework, mocking-library, mocking-utility, mockgen and protocol, alongside performance words like efficient, fast, performant and scaleable.

## Running it is a source directory and a destination file

Mockolo is a command line executable, and the usage model is short. You pass in a list of source file directories or file paths of a build target, and the destination filepath for the mock output.

The example in the README is:

```bash
./mockolo -s myDir -d ./OutputMocks.swift -x Images Strings
```

That parses all the source files in the myDir directory, excluding any files ending with Images or Strings in the file name, such as MyImages.swift, and generates mocks to a file at OutputMocks.swift in the current directory.

The -x flag is worth noticing because it is the escape hatch that makes the tool usable in a real repository. Localised string files and image asset names are typically generated or vendored, and they are exactly the kind of file that makes a parser slow and a mock generator confused. Excluding them by suffix is a blunt instrument that works.

The full option list in the help output shows how much control there is, and the help text is quoted in the README so you can see the shape of it. The concurrency limit is set with -j and defaults to 1, which tells you that the tool is single threaded by default and lets you raise it. The annotation option sets a custom annotation string used to indicate if a type should be mocked, defaulting to @mockable, so the convention that decides what gets mocked is configurable. There are flags for custom imports and for excluded imports, a header option for a custom documentation comment at the top of the generated file, a logging level, a macro option that wraps the generated content in #if and #endif guards, an allow-set-call-count flag for the generated call count variables, and flags for enabling args history and for controlling whether Combine streams are generated.

That last group is where the Swift specific behaviour lives. Args history records the arguments each method was called with, and the Combine flags control whether the mock publishes streams, which matters because a mock that generates Combine publishers by default will not compile in a project that does not use Combine.

## Five ways to install, and the plugin is the interesting one

The build and install section gives five options, and the fourth and fifth are the ones most people will use.

By Mint:

```bash
$ mint install uber/mockolo
$ mint run uber/mockolo mockolo -h // see commandline input options below
```

By Homebrew:

```bash
$ brew install mockolo
```

Then there is the Swift Package Manager route as a Build Tools Plugin, which is the one that changes how the tool is used rather than just where the binary lives. You add a binaryTarget and a plugin definition to your Package.swift, and the binary url and checksum can be found in the releases page:

```swift
targets: [
    ...
    .plugin(
        name: "RunMockolo",
        capability: .buildTool(),
        dependencies: [.target(name: "mockolo")]
    ),
    .binaryTarget(
        name: "mockolo",
        url: "...",
        checksum: "..."
    ),
```

You then implement the plugin and specify the necessary directories in the arguments, in a file at Plugins/RunMockolo/RunMockoloPlugin.swift:

```swift
import PackagePlugin

@main struct RunMockoloPlugin: BuildToolPlugin {
    func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
        let generatedSourcePath = context.pluginWorkDirectory.appending("GeneratedMocks.swift")
        let packageRoot = context.package.directory

        return [
            .prebuildCommand(
                displayName: "Run mockolo",
                executable: try context.tool(named: "mockolo").path,
                arguments: [
                    "-s", packageRoot.appending("Sources", "MyModule").string,
                    "-d", generatedSourcePath,
                ],
                outputFilesDirectory: context.pluginWorkDirectory
            ),
        ]
    }
}
```

Finally you add the plugin to the target requiring mockolo, listing .plugin(name: "RunMockolo") in the target's plugins array.

The fourth option is simply to download the binary from the Release tab, and the fifth is to clone and build it yourself:

```bash
$ git clone https://github.com/uber/mockolo.git
$ cd mockolo
$ swift build -c release
$ .build/release/mockolo -h  // see commandline input options below
```

The plugin is the interesting option because it makes mock generation a build step rather than a separate command you remember to run. The generated file lands in the plugin work directory, and the prebuild command runs before the target compiles. The trade is that the mocks are regenerated on every build rather than committed, which changes who reviews them.

## The disclaimer is the honest part of the documentation

There is a disclaimer section, and it is short enough to quote in substance because it defines the terms of the whole project.

This project may contain unstable APIs which may not be ready for general use. Support and new releases may be limited.

That is a strong statement from a project whose repository is under the uber organisation and whose version is 2.7.0. It tells you two things: the API surface may change without notice, and the release cadence is not guaranteed.

The release record supports the first point rather than the second. The recent versions are 2.7.0 on 2026-10-09, 2.6.1 on 2026-03-24 and 2.6.0 on 2026-03-24, so a patch release and a minor release shipped within about half an hour of each other, which is the pattern of a tool that is being adjusted quickly rather than one on a slow schedule. The last push to the repository was on 2026-10-09 and the repository is not archived.

The repository badges are worth reading too. There is a CII Best Practices badge, which is a third-party assessment of the project's processes rather than of its code, a GitHub Actions build status badge on the master branch, a FOSSA badge for licence and dependency scanning, and an Apache 2.0 licence badge. The licence field on the project record is Apache-2.0, and the repository carries both a LICENSE.txt and a NOTICE.txt alongside a CODE_OF_CONDUCT.md and a CONTRIBUTING.md.

The top level listing also shows a Sources directory, a Tests directory, a bundle directory, a Package.swift and Package.resolved, an install-script.sh, and a build workflow in the .github directory. A NOTICE file is what you expect from a project with a scanned dependency tree, and a Package.resolved is what pins the exact versions of the packages the tool itself depends on, which is the thing that makes a build reproducible.

The system requirements are explicit too: Swift 5.10 or later, Xcode 15.3 or later, macOS 13.0 or later and Linux, with support included for the Swift Package Manager.

## Where Mockolo is the wrong tool

Three situations rule it out.

The first is a small project. The tool's whole justification is a codebase of over 2 million lines and over ten thousand protocols. A project with twenty protocols has a hand written mock file that is shorter than the configuration you would write, and a hand written mock is easier to review, easier to change and easier to put a meaningful default return value in. The performance argument is real but it is irrelevant at that size.

The second is when the mock needs behaviour rather than a stub. What a mock generator produces is a type that conforms to your protocol, records what was called, and returns a default. If the test depends on a specific return value, or on a sequence of returns, or on a side effect, you are writing that by hand afterwards, in which case you have the generator's output plus your own file, and the review burden of the generated file is now doubled.

The third is an environment where generated code cannot be committed. If your policy is that no generated file is ever checked in, and regeneration is not wired into your build, then the mocks in your repository will drift from your protocols. The Swift Package Manager plugin is the answer to that, and it is a real answer, but it means every build runs the generator, which means every build depends on the generator working, which means a bug in the generator breaks your build rather than producing a stale file.

There is one more thing the documentation does not say, and an adopter should work out for themselves. The generated output is Swift code that must compile. The help output shows options for a custom header comment, for excluded and custom imports, for a macro guard, and for annotation-driven behaviour, which tells you the author expects the generated file to need adjusting to fit a project. That adjusting is not documented in the README, so expect to spend time on it.

## Conclusion

Mockolo fits a Swift team with a large protocol surface that wants generated mocks committed to the repository and regenerated in seconds rather than minutes, and it fits teams that already treat mock generation as a build step through the Swift Package Manager plugin. It is the wrong choice for a small project with a handful of protocols, where the hand written mock is shorter than the configuration, and it is the wrong choice if you need hand written behaviour, because what it produces is stubs with recorded calls rather than implementations you can reason about. Before adopting it, run the generator over your real target and read the output rather than the documentation, since the project states plainly that it may contain unstable APIs which may not be ready for general use and that support and new releases may be limited.

## FAQ

### What is Mockolo and what does it generate?

Mockolo is an efficient mock generator for Swift. Swift does not provide mocking support, so Mockolo provides a fast way to autogenerate mock objects that can be tested in your code, parsing your source with SwiftSyntax and writing mock implementations to a destination file.

### How do I run Mockolo?

Pass a list of source file directories or file paths and a destination path. The example is ./mockolo -s myDir -d ./OutputMocks.swift -x Images Strings, which parses everything in myDir, excludes files ending with Images or Strings in the name, and writes the mocks to OutputMocks.swift.

### How can I install Mockolo?

Five routes are documented: mint install uber/mockolo, brew install mockolo, use it as a Swift Package Manager build tool plugin with a binaryTarget, download the binary from the Release tab, or clone the repository and run swift build -c release. The system requirements are Swift 5.10 or later, Xcode 15.3 or later, and macOS 13.0 or later, or Linux.

### Can I run Mockolo as part of my Swift build?

Yes, as a Swift Package Manager build tool plugin. You add a binaryTarget and a plugin definition to Package.swift, implement a RunMockoloPlugin that returns a prebuild command running the executable against your sources, and then list .plugin(name: "RunMockolo") in the plugins array of the target that needs the mocks.

### Is Mockolo still being developed?

The last push to the repository was on 2026-10-09 and the repository is not archived, with v2.7.0 released the same day after v2.6.1 and v2.6.0. The licence is Apache-2.0, and the project states that it may contain unstable APIs which may not be ready for general use and that support and new releases may be limited.

## Sources

- [Issues](https://github.com/uber/mockolo/issues)
- [License: Apache-2.0](https://github.com/uber/mockolo/blob/master/LICENSE)
- [README](https://github.com/uber/mockolo/blob/master/README.md)
- [Releases](https://github.com/uber/mockolo/releases)
- [uber/mockolo on GitHub](https://github.com/uber/mockolo)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/uber-mockolo
