Mockolo とは
Mockoloの説明は公式READMEに頼ります。
Mockolo is an efficient mock generator for Swift. Swift doesn't provide mocking support, and Mockolo provides a fast and easy way to autogenerate mock objects that can be tested in your code. One of the main objectives of Mockolo is fast performance. Unlike other frameworks, Mockolo provides highly performant and scalable generation of mocks via a lightweight commandline tool, so it can run as part of a linter or a build if one chooses to do so.
日本語訳
Mockolo は、Swift 用の効率的なモック ジェネレーターです。Swift はモックをサポートしていませんが、Mockolo はコード内でテストできるモック オブジェクトを高速かつ簡単に自動生成する方法を提供します。Mockolo の主な目的の 1 つは、高速なパフォーマンスです。他のフレームワークとは異なり、Mockolo は軽量のコマンドライン ツールを介して、非常に高性能でスケーラブルなモック生成を提供するため、必要に応じてリンターまたはビルドの一部として実行できます。
通信を行う処理などは、単体テストなどではそのまま使うと結果を制御するのが難しいので、大体はMockクラスを作って戻り値を制御したりすることでテストの安定性などを担保しつつ通信などが絡む振る舞いのテストを行います。
ただMockクラスをいちいち作るのは大変という問題もあります。(XcodeはMockの生成をサポートしていないので)
MockoloではMockクラスの自動生成を行ってくれるため、テストを行うためのMock作成のコストを大幅に削減できるようになります。
SPM で導入するモチベーション
そもそも、Swift のライブラリ管理ツールは SPM の他にも CocoaPods や Carthage、ツール系で言えば、Mint や Bundler などたくさんあります。
管理ツールによっては対応していないライブラリなどもありますが、SPMの場合は、Apple 純正のツールということもあり、最近だと SPM で導入できるライブラリはかなり増えています。(タイトルの通りMockoloもSPMに対応しています!)
ライブラリ管理ツールはできれば統一できたほうが、ライブラリの管理も楽になるので
であれば、近年でデファクトスタンダードになりつつある SPM で導入した方が、ライブラリ管理ツールの統一に一歩近づくという判断でSPMによる導入を検討してみました。
SPM での導入手順
公式の README に基本的な導入方法は記載していたので、これを参考に導入していきます。
手順 1. Package.swift を作成する
Mockolo を使う対象が SPM の Package.swift で管理されている場合は、この手順で新しく Package.swift を作らずとも、もともとある Package.swift に Mockolo を追加できるので必要ない
Xcode
-> File
-> New
-> Package
から Package.swift のプロジェクトを作成します。
作成された Pakcage.swift の例が以下です。
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MockoloPluginLib",
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "MockoloPluginLib",
targets: ["MockoloPluginLib"]),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "MockoloPluginLib"),
.testTarget(
name: "MockoloPluginLibTests",
dependencies: ["MockoloPluginLib"]
),
]
)
手順 2. Package.swift に Mockolo の依存を追加する
dependencies
にmockolo
を追加します。
バージョンは 2025/3/22 時点の最新バージョンである2.3.1
を指定します。
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MockoloPluginLib",
products: [
.library(
name: "MockoloPluginLib",
targets: ["MockoloPluginLib"]),
],
+ dependencies: [
+ .package(url: "https://github.com/uber/mockolo", exact: "2.3.1")
+ ],
targets: [
.target(
name: "MockoloPluginLib"),
.testTarget(
name: "MockoloPluginLibTests",
dependencies: ["MockoloPluginLib"]
),
]
)
手順 3. ターゲットに Plugin を追加
target
に Mockolo 実行用の plugin を定義します。
また必要ないターゲットを全て削除します(今回必要なのは Mockolo を実行するための plugin のみのため)
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MockoloPluginLib",
products: [
- .library(
- name: "MockoloPluginLib",
- targets: ["MockoloPluginLib"]),
- products: [
+ .plugin(name: "RunMockolo", targets: ["RunMockolo"])
],
],
dependencies: [
.package(url: "https://github.com/uber/mockolo", exact: "2.3.1")
],
targets: [
- .target(
- name: "MockoloPluginLib"),
- .testTarget(
- name: "MockoloPluginLibTests",
- dependencies: ["MockoloPluginLib"]
- ),
+ .plugin(
+ name: "RunMockolo",
+ capability: .buildTool(),
+ dependencies: [.target(name: "mockolo")]
+ ),
+ .binaryTarget(
+ name: "mockolo",
+ url: "https://github.com/uber/mockolo/releases/download/2.3.1/mockolo-macos.artifactbundle.zip",
+ checksum: "daefaebdb24bf0f0a5f830523330b81850e9e95a3e2fc2011a50054309a55eae"
+ ),
]
)
binaryTarget
に指定した値は、README でも書いてあるが、リリースノートに記載されているものを設定しました。
.binaryTarget(
name: "mockolo",
url: "https://github.com/uber/mockolo/releases/download/2.3.1/mockolo-macos.artifactbundle.zip",
checksum: "daefaebdb24bf0f0a5f830523330b81850e9e95a3e2fc2011a50054309a55eae"
),
ついでにここで、先ほど削除したターゲットのフォルダは全て削除しておきます。
Sources
とTests
のフォルダをそのまま削除します(以下キャプチャの赤枠箇所)
手順 4. plugin の実装を追加する
Plugins
ディレクトリを作成し、その配下にRunMockolo
ディレクトリを作成します。
以下のようなディレクトリ構成になるはずです。(Plugins
配下のディレクトリ名は Package.swift で定義した plugin のターゲット名が入るので適宜変更すること)
RunMockolo
に以下内容のファイルを作成します。
import Foundation
import PackagePlugin
@main
struct RunMockoloPlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
let engine = try RunMockoloEngine(pluginWorkDirectory: context.pluginWorkDirectoryURL,
targetUrl: URL(string: target.directory.string)!,
mockoloPath: context.tool(named: "mockolo").url)
return engine.build()
}
}
#if canImport(XcodeProjectPlugin)
import XcodeProjectPlugin
extension RunMockoloPlugin: XcodeBuildToolPlugin {
func createBuildCommands(context: XcodeProjectPlugin.XcodePluginContext,
target: XcodeProjectPlugin
.XcodeTarget) throws -> [PackagePlugin.Command] {
let engine = try RunMockoloEngine(pluginWorkDirectory: context.pluginWorkDirectoryURL,
targetUrl: context.xcodeProject.directoryURL,
mockoloPath: context.tool(named: "mockolo").url)
return engine.build()
}
}
#endif
struct RunMockoloEngine {
let pluginWorkDirectory: URL
let targetUrl: URL
let mockoloPath: URL
func build() -> [Command] {
let generatedSourcePath = pluginWorkDirectory.appending(component: "GeneratedMocks.swift")
return [.prebuildCommand(displayName: "Run mockolo",
executable: mockoloPath,
arguments: ["-s", targetUrl.path(),
"-d", generatedSourcePath.path()],
outputFilesDirectory: pluginWorkDirectory)]
}
}
やっていることは、plugin を使用するターゲット内でビルドしたときに、モックを生成するだけです。
XcodeBuildToolPlugin
に準拠した拡張を実装した理由は、project ファイルのビルドフェーズのRun Build Tool Plug-ins
にで使用できるようにするためです。
XcodeBuildToolPlugin
に準拠していないと、Plugin doesn't support Xcode projects
のエラーが発生してしまいます。
ここまでの手順で、一旦 Mockolo を使うための Plugin を作成する作業は終了です。
project ファイルで Mockolo を使う
以下のように、package dependencies から上記手順で作成したローカルの SPM モジュール(Package.swift が入っているディレクトリ)を指定します。
あとは、アプリケーションターゲットのビルドフェーズの設定を開いて、Run Build Tool Plug-ins
でRunMockolo
を指定するだけです。
これができたらアプリケーションターゲットのビルドを行うと、/// @mockable
がついたprotocol
のモックが自動生成されるようになります。
導入後の詳しい使い方は以下記事がとてもわかりやすかったです。
SPM モジュール内で Mockolo を使う
Package.swift の target 定義で、以下のように plugin を指定すれば、ビルド時にモックを自動生成してくれるようになります。
targets: [
...
.target(
name: "RxSwiftExample",
dependencies: [
...
],
plugins: [
.plugin(name: "RunMockolo")
]
),
...
]
SPM で Mockolo を使用している Package.swift の例は以下の通り。
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "RxSwiftExample",
platforms: [.iOS(.v15)],
products: [
.library(
name: "RxSwiftExample",
targets: ["RxSwiftExample"]),
],
dependencies: [
.package(url: "https://github.com/ReactiveX/RxSwift.git", exact: "6.9.0"),
.package(url: "https://github.com/uber/mockolo", exact: "2.3.1")
],
targets: [
.plugin(
name: "RunMockolo",
capability: .buildTool(),
dependencies: [.target(name: "mockolo")]
),
.binaryTarget(
name: "mockolo",
url: "https://github.com/uber/mockolo/releases/download/2.3.1/mockolo-macos.artifactbundle.zip",
checksum: "daefaebdb24bf0f0a5f830523330b81850e9e95a3e2fc2011a50054309a55eae"
),
.target(
name: "RxSwiftExample",
dependencies: [
"RxSwift",
.product(name: "RxCocoa", package: "RxSwift")
],
plugins: [
.plugin(name: "RunMockolo")
]
),
.testTarget(
name: "RxSwiftExampleTests",
dependencies: ["RxSwiftExample"]
),
]
)
SPM モジュール内で Mockolo の Plugin を使っているリポジトリは以下になります。