| name | sake-testing |
| description | Use when writing or running tests for the Sake project — unit tests, macro expansion tests, integration tests, test helpers, and CI test commands. |
Sake Testing Patterns
Test Targets
| Target | Tests | Framework |
|---|
SakeTests | Command, CommandRunner, CommandsPreprocessor, ClosestMatchFinder, CommandListFormatter | XCTest |
SakeCLILibraryTests | ConfigManager, ConfigResolver, SakeAppManager, FileHandle | XCTest |
SakeMacrosTests | @CommandGroup macro expansion | XCTest + SwiftSyntaxMacrosTestSupport |
IntegrationTests | Full CLI execution via subprocess | XCTest + swift-subprocess |
Running Tests
sake test
sake build_tests
sake unit_tests --skip-build
sake integration_tests --skip-build
Direct swift commands:
swift test --filter "^(?!.*\bIntegrationTests\b).*"
swift test --filter IntegrationTests
swift test
Unit Test Patterns
Command Execution Tracking
nonisolated(unsafe) var runnedCommands: [String] = []
let command = Command(
skipIf: { _ in runnedCommands.append("skipIf"); return false },
run: { _ in runnedCommands.append("run") },
)
let runner = CommandRunner(command: command, context: .empty)
try await runner.run()
XCTAssertEqual(runnedCommands, ["skipIf", "run"])
Empty Context Helper
private extension Command.Context {
static var empty: Command.Context {
Command.Context(
arguments: [],
environment: [:],
appDirectory: "",
runDirectory: "",
storage: .init(),
interruptionHandler: .init(processMonitor: .init()),
)
}
}
Storage Sharing Tests
let dep = Command(run: { context in
context.storage["key"] = "value"
})
let main = Command(dependencies: [dep], run: { context in
XCTAssertEqual(context.storage["key"] as? String, "value")
})
Macro Expansion Tests
import SakeMacros
import SwiftSyntaxMacrosTestSupport
assertMacroExpansion(
inputSource,
expandedSource: expectedOutput,
macros: ["CommandGroup": CommandGroupMacro.self],
)
CLI Library Tests
Use protocol-based mocking for SakeAppManagerFileHandle and SakeAppManagerCommandExecutor. Tests verify:
- Config resolution priority (CLI > ENV > file)
- Mutual exclusion validation
- File existence checks
- Build lifecycle decisions
Integration Tests
Full end-to-end: compile SakeApp, execute commands, verify output. Use swift-subprocess to run sake binary.
All Tests Are Async
All test methods use async throws:
func testSomething() async throws {
}