Getting started
Your first recorded call
Create a generated mock, register one result, call the original protocol member, then compare the recorded invocation with a typed expectation.
On this page
Requirements
Use Swift 6.3 or newer. Mocksmith supports Linux, iOS 17, macOS 13, tvOS 17, and watchOS 10 or newer.
Installation
Add the current release to your package dependencies.
Package.swift
.package(
url: "https://github.com/modern-swift-dev/mocksmith-swift.git",
exact: "1.0.0"
)
Add Mocksmith, exactly one runner adapter, and the build plugin to your test target. This example uses Swift Testing. Choose MocksmithXCTest instead for XCTest.
Package.swift
.testTarget(
name: "AppTests",
dependencies: [
.product(name: "Mocksmith", package: "mocksmith-swift"),
.product(name: "MocksmithTesting", package: "mocksmith-swift")
],
plugins: [
.plugin(name: "MocksmithBuildPlugin", package: "mocksmith-swift")
]
)
@Mockable defaults to .buildPlugin, which generates complete mocks as Swift source before compilation and avoids repeated compiler macro expansion of their implementations. The plugin also resolves inherited protocols and composition aliases in the target’s reachable SwiftPM source dependencies. Attach it to every target that uses the default @Mockable, including SwiftPM dependency targets built from an Xcode app.
The default requires unconditional, top-level internal, package, or public protocols whose requirement types are accessible from the generated source file. When migrating existing direct mocks, add the plugin to their declaring targets. For private, fileprivate, nested, or conditional direct protocols, explicitly use compiler macro expansion:
@Mockable(.macro)
private protocol WeatherService {
func temperature(for city: String) async throws -> Double
}
Direct protocols using @Mockable(.macro) do not require the plugin. Protocols with custom inheritance still require it.
First call snapshot
Mocksmith does not write snapshot files. Its snapshot of test behavior is the typed invocation history held by each mock. The test below registers a result, makes one call, and verifies the recorded argument.
WeatherServiceTests.swift
import Mocksmith
import MocksmithTesting
import Testing
@Mockable
protocol WeatherService {
func temperature(for city: String) async throws -> Double
}
@Test func readsTheTemperature() async throws {
let weather = WeatherServiceMock()
Given(weather).temperature(for: .value("Toronto")).willReturn(20)
let result = try await weather.temperature(for: "Toronto")
#expect(result == 20)
Verify(weather, 1).temperature(for: .value("Toronto"))
}
Recording
Calls reads a member’s invocation history without marking calls as verified. Use it when a test must wait for an async call or inspect captured arguments before the final assertion.
Inspecting call history
let calls = Calls(weather).temperature(for: .value("Toronto"))
try await calls.waitForCount(1, timeout: .seconds(1))
let city = try calls.onlyArgument
Verify(weather, 1).temperature(for: .value(city))
VerifyNoMoreInteractions(weather)
Comparison workflow
-
Use
Givento register the outcome required by the test. -
Call the mock through the protocol API.
-
Use
Verifywith a matcher and count. AddVerifyNoMoreInteractionswhen every call should be accounted for.
Recorded call compared with the expectation
Expected
temperature(for: “Toronto”)
count: exactly 1
Recorded
temperature(for: “Toronto”)
1 matching call
Mocks are strict by default. An unstubbed throwing member reports MockError.unstubbed. Nonthrowing members stop because their signatures have no legal error result. For setup-heavy tests, pass defaults: .voidAndOptional to relax only unstubbed nonthrowing Void and optional results.
Continue to the examples for matchers, call sequences, and property state.