-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
611 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# Mocked | ||
|
||
Mocked is a Swift compiler macro that automatically generates mock implementations for protocols. This can be especially useful for unit testing, allowing you to easily create mock objects to verify behavior and interactions in your tests. | ||
|
||
## Features | ||
|
||
- **Automatic Mock Generation**: Simply annotate your protocol with `@Mocked`, and a mock implementation will be generated. | ||
- **Supports Properties and Methods**: Generates mock versions of properties and methods, including `async` and `throws` variants. | ||
- **Configurable Behavior**: Easily override behavior by providing closures during initialization of the mock. | ||
|
||
## Installation | ||
|
||
To use Mocked in your project, add it as a dependency using Swift Package Manager. Add the following to your `Package.swift` file: | ||
|
||
```swift | ||
.package(url: "https://github.com/0xLeif/Mocked.git", from: "1.0.0") | ||
``` | ||
|
||
And add it as a dependency to your target: | ||
|
||
```swift | ||
.target( | ||
name: "YourTargetName", | ||
dependencies: [ | ||
"Mocked" | ||
] | ||
) | ||
``` | ||
|
||
## Usage | ||
|
||
To generate a mock for a protocol, simply annotate it with `@Mocked`: | ||
|
||
```swift | ||
@Mocked | ||
protocol MyProtocol { | ||
var title: String { get set } | ||
func performAction() -> Void | ||
} | ||
``` | ||
|
||
This will generate a mock struct named `MockedMyProtocol` that conforms to `MyProtocol`. You can use this mock in your unit tests to validate behavior. | ||
|
||
### Example | ||
|
||
```swift | ||
@Mocked | ||
protocol MyProtocol { | ||
var title: String { get set } | ||
func performAction() -> Void | ||
} | ||
|
||
let mock = MockedMyProtocol( | ||
title: "Test Title", | ||
performAction: { print("Action performed") } | ||
) | ||
|
||
mock.performAction() // Output: "Action performed" | ||
``` | ||
|
||
### Edge Cases and Warnings | ||
|
||
- **Non-Protocol Usage**: The `@Mocked` macro can only be applied to protocols. Using it on other types will result in a compilation error. | ||
- **Unimplemented Methods**: Any method that is not overridden will call `fatalError()` if invoked. Ensure all required methods are implemented when using the generated mock. | ||
- **Async and Throwing Methods**: The generated mocks handle `async` and `throws` methods appropriately, but be sure to provide closures that match the method signatures. | ||
|
||
## Contributing | ||
|
||
Contributions are welcome! If you have suggestions, issues, or improvements, feel free to open a pull request or issue on the [GitHub repository](https://github.com/0xLeif/Mocked). | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more information. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,48 @@ | ||
// The Swift Programming Language | ||
// https://docs.swift.org/swift-book | ||
|
||
/// A macro that produces both a value and a string containing the | ||
/// source code that generated the value. For example, | ||
/// The `Mocked` macro is used to automatically generate a mocked implementation of a protocol. | ||
/// | ||
/// #stringify(x + y) | ||
/// This macro attaches a peer struct prefixed with `Mocked` that provides implementations of all the methods and properties defined in the protocol. | ||
/// | ||
/// produces a tuple `(x + y, "x + y")`. | ||
@freestanding(expression) | ||
public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MockedMacros", type: "StringifyMacro") | ||
/// # Usage | ||
/// Apply the `@Mocked` attribute to a protocol declaration to generate a mock implementation of that protocol. This mock implementation can be used for unit testing purposes to easily verify interactions with the protocol methods and properties. | ||
/// | ||
/// Example: | ||
/// ```swift | ||
/// @Mocked | ||
/// protocol MyProtocol { | ||
/// var title: String { get set } | ||
/// func performAction() -> Void | ||
/// } | ||
/// ``` | ||
/// | ||
/// The code above will generate a `MockedMyProtocol` struct that implements `MyProtocol`. | ||
/// | ||
/// # Edge Cases and Warnings | ||
/// - **Non-Protocol Usage**: This macro can only be applied to protocol definitions. Attempting to use it on other types, such as classes or structs, will lead to a compilation error. | ||
/// - **Unimplemented Methods**: Any method that is not explicitly overridden will call `fatalError()` when invoked, which will crash the program. Ensure all necessary methods are mocked when using the generated struct. | ||
/// - **Async and Throwing Methods**: The macro correctly handles protocols with `async` and/or `throws` functions. Be mindful to provide appropriate closures during initialization. | ||
/// | ||
/// # Example of Generated Code | ||
/// For the protocol `MyProtocol`, the generated mock implementation would look like this: | ||
/// ```swift | ||
/// struct MockedMyProtocol: MyProtocol { | ||
/// var title: String | ||
/// private let performActionOverride: (() -> Void)? | ||
/// | ||
/// init(title: String, performAction: (() -> Void)? = nil) { | ||
/// self.title = title | ||
/// self.performActionOverride = performAction | ||
/// } | ||
/// | ||
/// func performAction() { | ||
/// guard let performActionOverride else { | ||
/// fatalError("Mocked performAction was not implemented!") | ||
/// } | ||
/// performActionOverride() | ||
/// } | ||
/// } | ||
/// ``` | ||
@attached(peer, names: prefixed(Mocked)) | ||
public macro Mocked() = #externalMacro( | ||
module: "MockedMacros", | ||
type: "MockedMacro" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,37 @@ | ||
import Mocked | ||
|
||
let a = 17 | ||
let b = 25 | ||
protocol ThisBreaksShit { | ||
var broken: String { get } | ||
} | ||
|
||
let (result, code) = #stringify(a + b) | ||
@Mocked | ||
protocol SomeParameter: Sendable { | ||
var title: String { get set } | ||
var description: String { get } | ||
|
||
print("The value \(result) was produced by the code \"\(code)\"") | ||
func someMethod() | ||
func someMethod(parameter: Int) | ||
func someMethod(with parameter: Int) | ||
|
||
func someOtherMethod() throws -> String | ||
func someOtherMethod() async throws -> String | ||
|
||
func someAsyncMethod() async -> String | ||
|
||
func someOptionalMethod() -> String? | ||
} | ||
|
||
Task { @MainActor in | ||
let mockedParameter = MockedSomeParameter( | ||
title: "Hello", | ||
description: "Descrip", | ||
someMethodParameter: { print("\($0)") }, | ||
someOtherMethodAsyncThrows: { "?" } | ||
) | ||
|
||
mockedParameter.someMethod(parameter: 3) | ||
let value = try await mockedParameter.someOtherMethod() | ||
|
||
print(value) | ||
|
||
} |
Oops, something went wrong.