Hey everyone,
I'm excited to introduce SwiftQC, a property-based testing framework I built specifically for Swift 6+ and modern Swift development.
Repository: https://github.com/Aristide021/SwiftQC
Documentation: Complete guides and examples
Why Property-Based Testing?
As developers, we usually write tests by coming up with specific inputs and checking the outputs. SwiftQC automatically generates hundreds of test cases, and when failures occur, intelligently shrinks them to the minimal counterexample, often revealing edge cases you'd never think to test manually.
What SwiftQC Brings To The Table
Complete Stateful Testing Engine
Confidently test complex user flows and stateful APIs.** SwiftQC's stateful engine lets you model your system's behavior, then automatically generates sequences of commands to find elusive, state-related bugs. When a test fails, its powerful command sequence shrinker pinpoints the minimal steps needed to reproduce the error, saving you hours of debugging.
Concurrent System Verification
Built-in parallel testing runner helps discover race conditions and concurrency bugs by executing commands concurrently and detecting divergences from expected behavior.
Elegant, Composable Design
- Sendable-first architecture built for Swift 6+ concurrency
- Seamless integration with Swift Testing's issue reporting system
- Built on proven foundations - leverages PointFree's excellent
swift-gen
for composable generators - Ergonomic
Arbitrary
protocol makes most tests incredibly concise
Usage
Basic Property Testing:
import SwiftQC
import Testing
@Test
func additionIsCommutative() async {
await forAll("Integer addition is commutative") { (a: Int, b: Int) in
#expect(a + b == b + a)
}
}
Custom Types:
struct Point: Arbitrary, Sendable, Equatable {
let x: Int, y: Int
static var gen: Gen<Point> {
zip(Int.gen, Int.gen).map(Point.init)
}
static var shrinker: any Shrinker<Point> {
Shrinkers.map(
from: Shrinkers.tuple(Shrinkers.int, Shrinkers.int),
transform: Point.init,
reverse: { ($0.x, $0.y) }
)
}
}
@Test
func pointDistanceIsCommutative() async {
await forAll("Distance calculation is commutative") { (p1: Point, p2: Point) in
#expect(distance(p1, p2) == distance(p2, p1))
}
}
Stateful Testing:
// Define your state model
struct CounterModel: StateModel {
typealias State = Int
typealias SUT = MyCounter
// ... command generation, state transitions, etc.
}
@Test
func counterMaintainsConsistency() async {
let result = await stateful(
"Counter behaves according to model",
sutFactory: { MyCounter() }
)
// SwiftQC handles command sequence generation, execution,
// divergence detection, and automatic shrinking
}
Rich Ecosystem Support
SwiftQC provides Arbitrary
conformances for extensive Swift standard library coverage:
- Numeric types: All integer and floating-point variants, including
Decimal
andCGFloat
- Text processing:
String
,Character
,Unicode.Scalar
- Collections:
Array
,Dictionary
,Set
,Optional
,Result
- Foundation types:
Date
,Data
,UUID
, and more
Getting Started
Installation:
// Package.swift
.package(url: "https://github.com/Aristide021/SwiftQC.git", from: "1.0.0")
Requirements:
- Swift 6.0+
- Platforms: macOS 12+, iOS 13+, tvOS 13+, watchOS 6+
- Works with both Swift Testing and XCTest
Explore the Examples:
- BasicUsage - Fundamentals and custom types
- StatefulExample - State machine testing
- ParallelExample - Concurrent system verification
Each example is a complete Swift package you can run immediately:
cd Examples/BasicUsage && swift test
Community & Feedback
I would greatly appreciate your feedback, contributions, and real-world testing. Please try it out and share your thoughts, whether through GitHub issues, discussions, or the forums.