Skip to content

gokulnair2001/SwiftMock

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Logo

SwiftMock

Swift Platform License Swift Package Manager

A Swift command-line tool that automatically generates mock implementations for protocols marked with /// @mockable comment. SwiftMock simplifies unit testing by eliminating the need to manually write mock objects, allowing you to focus on writing tests instead of boilerplate code.

Why SwiftMock?

Writing mock implementations for protocols is repetitive and time-consuming. SwiftMock automates this process by:

  • Automatically generating mock classes from your protocol definitions
  • Tracking method calls with call counts and received arguments
  • Supporting custom handlers for flexible test scenarios
  • Handling complex method signatures including throws, return types, and multiple parameters

Installation

Swift Package Manager

Add SwiftMock to your Package.swift:

dependencies: [
    .package(url: "https://github.com/yourusername/SwiftMock.git", from: "1.0.0")
]

Building from Source

git clone https://github.com/yourusername/SwiftMock.git
cd SwiftMock
swift build -c release
cp .build/release/swiftmock /usr/local/bin/

Usage

1. Mark Protocols with /// @mockable

Add the /// @mockable documentation comment above protocols you want to mock:

/// @mockable
protocol UserService {
    func fetchUser(id: String) throws -> User
    func deleteUser(id: String) async throws
    func updateUserName(_ name: String, for id: String)
}

2. Generate Mocks

Run the SwiftMock command:

swiftmock --source ./Sources --output ./Tests/Mocks/GeneratedMocks.swift

3. Use Generated Mocks in Tests

func testUserFetch() {
    let mockService = UserServiceMock()
    
    // Set up mock behavior
    mockService.fetchUserHandler = { id in
        return User(id: id, name: "Test User")
    }
    
    // Execute test
    let user = try mockService.fetchUser(id: "123")
    
    // Verify
    XCTAssertEqual(mockService.fetchUserCallCount, 1)
    XCTAssertEqual(mockService.fetchUserReceivedArguments.first, "123")
    XCTAssertEqual(user.name, "Test User")
}

Command-Line Flags

Flag Short Description
--source -s Required. Root directory to scan for Swift files
--output -o Required. Output file path for generated mocks
--exclude -e Comma-separated list of folders to exclude (e.g., Build,Archive)
--verbose -v Enable verbose output for debugging
--per-file Generate a separate mock file for each protocol
--force Force generation on every run (skips incremental build check)

Examples

The following examples demonstrate various ways to use SwiftMock depending on your project structure and requirements:

Basic usage:

swiftmock -s ./Sources -o ./Tests/Mocks/Mocks.swift

Exclude specific folders:

swiftmock -s ./Sources -o ./Tests/Mocks/Mocks.swift -e "ThirdParty,Generated"

Generate separate files per protocol:

swiftmock -s ./Sources -o ./Tests/Mocks/ --per-file

Verbose output with forced generation:

swiftmock -s ./Sources -o ./Tests/Mocks/Mocks.swift -v --force

Example Generated Mock

public final class UserServiceMock: UserService {
    public init() {}
    
    var fetchUserCallCount = 0
    var fetchUserHandler: ((String) throws -> User)?
    var fetchUserReceivedArguments: [String] = []
    
    func fetchUser(id: String) throws -> User {
        fetchUserCallCount += 1
        fetchUserReceivedArguments.append(id)
        guard let handler = fetchUserHandler else {
            fatalError("fetchUserHandler must be set before calling fetchUser")
        }
        return try handler(id)
    }
}

Important Notes

  • /// @mockable Comment: Only protocols marked with /// @mockable documentation comment will be processed. Ensure you add this comment directly above the protocol declaration.

  • Incremental Generation: By default, SwiftMock uses incremental builds to skip generation if source files haven't changed. Use --force to override this behavior.

  • Handler Requirements: For methods with return types, you must set the handler before calling the method, or it will trigger a fatal error. This ensures tests explicitly define expected behavior.

  • File Organization: When using --per-file, ensure the output path is a directory. SwiftMock will create separate files named {ProtocolName}Mock.swift.

  • Excluded Paths: Use the --exclude flag to skip folders like build outputs, third-party dependencies, or generated code directories.

  • Swift Version: Requires Swift 6.1 or later and macOS 13+.

  • Access Control: Generated mocks are marked public to ensure they can be used across module boundaries in your tests.

Integration with Xcode

Build Phase Script

Add a build phase script to automatically regenerate mocks:

  1. In Xcode, select your test target
  2. Go to Build PhasesNew Run Script Phase
  3. Add the following script:
if which swiftmock >/dev/null; then
    swiftmock -s "$SRCROOT/Sources" -o "$SRCROOT/Tests/Mocks/GeneratedMocks.swift"
else
    echo "warning: SwiftMock not installed"
fi

License

SwiftMock is released under the MIT License. See LICENSE file for details.


Made with ❤️ for the Swift testing community

About

A CLI tool to generate mock code in swift

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages