You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,7 +12,10 @@ Mocked is a Swift 6 compiler macro that automatically generates mock implementat
12
12
13
13
-**Automatic Mock Generation**: Simply annotate your protocol with `@Mocked`, and a mock implementation will be generated.
14
14
-**Supports Properties and Methods**: Generates mock versions of properties and methods, including `async` and `throws` variants.
15
+
-**Access Level Control**: You can specify the access level (`open`, `public`, `package`, `internal`, `fileprivate`, `private`) for the generated mock.
15
16
-**Configurable Behavior**: Easily override behavior by providing closures during initialization of the mock.
17
+
-**Support for Associated Types**: The `Mocked` macro handles protocols with associated types using generics.
18
+
-**Automatic Detection of Class Requirements**: If the protocol conforms to `AnyObject`, a class is generated instead of a struct, maintaining reference semantics.
The `Mocked` macro is used to automatically generate a mocked implementation of a protocol, including support for associated types and automatic detection of class requirements.
2
+
# `Mocked` Macro Documentation
3
3
4
-
This macro attaches a peer struct or class prefixed with `Mocked`, which provides implementations of all the methods and properties defined in the protocol. This is particularly useful for unit testing, where creating mock objects manually can be cumbersome and error-prone. With `@Mocked`, developers can easily generate mock implementations that allow precise control over protocol methods and properties, enabling more effective and focused testing.
4
+
The `Mocked` macro is a powerful tool used to automatically generate a mocked implementation of a protocol, simplifying unit testing by creating mock objects without the need for cumbersome manual coding. This macro includes support for associated types and automatic detection of class requirements, providing flexibility in testing and making it ideal for developers looking to reduce boilerplate and focus on writing effective tests.
5
5
6
-
# Usage
7
-
Apply the `@Mocked` attribute to a protocol declaration to generate a mock implementation of that protocol. The generated mock will have the same properties and methods as the protocol, but they can be overridden through closures provided during initialization. This mock implementation can be used for unit testing purposes to easily verify interactions with the protocol methods and properties.
6
+
## Features
7
+
8
+
- **Automatic Mock Generation**: The `Mocked` macro automatically generates a mock implementation for any protocol. This saves development time and reduces the amount of boilerplate code required for creating mock objects.
9
+
- **Access Level Control**: You can specify the access level (`open`, `public`, `package`, `internal`, `fileprivate`, `private`) for the generated mock, making it suitable for different testing needs and code visibility requirements.
10
+
- **Closure-Based Method Overrides**: Methods and properties of the protocol can be overridden by providing closures during the initialization of the generated mock, allowing precise control over method behavior in tests.
11
+
- **Support for Associated Types**: The `Mocked` macro handles protocols with associated types using generics, offering flexibility for mocking complex protocol requirements.
12
+
- **Automatic Detection of Class Requirements**: If the protocol conforms to `AnyObject`, the macro generates a class instead of a struct, ensuring reference semantics are preserved where needed.
13
+
- **Support for `async` and `throws` Methods**: The generated mock can handle methods marked as `async` or `throws`, allowing you to create mock behaviors for asynchronous operations or error scenarios.
14
+
- **Automatic Default Property Implementations**: Properties defined in the protocol are automatically backed by straightforward storage, which can be accessed and modified as needed.
15
+
16
+
## Usage
17
+
18
+
To use the `@Mocked` macro, simply attach it to a protocol declaration. The generated mock will provide all properties and methods specified in the protocol, which can be customized through closures provided during initialization. This allows for easy setup of mock behavior for testing.
19
+
20
+
### Example:
8
21
9
-
Example:
10
22
```swift
11
23
@Mocked
12
24
protocol MyProtocol {
@@ -15,26 +27,10 @@ protocol MyProtocol {
15
27
}
16
28
```
17
29
18
-
The code above will generate a `MockedMyProtocol` struct that implements `MyProtocol`. This struct allows defining the behavior of `performAction()` by providing a closure during initialization, making it easy to set up test scenarios without writing extensive boilerplate code.
30
+
The code above will generate a `MockedMyProtocol` struct that implements `MyProtocol`. This struct allows defining the behavior of `performAction()` by providing a closure during initialization, making it easy to set up test scenarios without extensive boilerplate code.
19
31
20
-
# Features
21
-
The `@Mocked` macro provides several key features:
22
-
23
-
- **Automatic Mock Generation**: Generates a mock implementation for any protocol, saving time and reducing boilerplate code.
24
-
- **Closure-Based Method Overrides**: Methods and properties can be overridden by providing closures during mock initialization, giving you full control over method behavior in different test scenarios.
25
-
- **Support for Associated Types**: Handles protocols with associated types by using Swift generics, providing flexibility for complex protocol requirements.
26
-
- **Automatic Detection of Class Requirements**: If the protocol conforms to `AnyObject`, the macro generates a class instead of a struct, ensuring reference semantics are maintained where needed.
27
-
- **Support for `async` and `throws` Methods**: The generated mock can handle methods marked as `async` or `throws`, allowing you to create mock behaviors that include asynchronous operations or errors.
28
-
- **Automatic Default Property Implementations**: Provides straightforward storage for properties defined in the protocol, which can be accessed and modified as needed.
29
-
30
-
# Edge Cases and Warnings
31
-
- **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.
32
-
- **Unimplemented Methods**: Any method that is not explicitly overridden will call `fatalError()` when invoked, which will crash the program. This behavior is intentional to alert developers that the method was called without being properly mocked. Always ensure that all necessary methods are mocked when using the generated struct to avoid runtime crashes. Mocks should only be used in tests or previews, where such crashes are acceptable for ensuring proper setup.
33
-
- **Async and Throwing Methods**: Be mindful to provide appropriate closures during initialization to match the behavior of `async` or `throws` methods. If no closure is provided, the default behavior will result in a `fatalError()`.
34
-
- **Value vs. Reference Semantics**: The generated mock defaults to being a struct, which means it follows value semantics. If the protocol requires reference semantics (e.g., it conforms to `AnyObject`), the macro will generate a class instead.
32
+
### Example of Generated Code:
35
33
36
-
# Example of Generated Code
37
-
For the protocol `MyProtocol`, the generated mock implementation would look like this:
- The `title` property is stored directly within the struct, allowing you to get or set its value just like a normal property.
64
-
- The `performAction` method uses a closure (`performActionOverride`) provided during initialization. If no closure is provided, calling `performAction()` will result in a `fatalError`, ensuring you never accidentally call an unmocked method.
58
+
## Access Level Control
59
+
60
+
The `@Mocked` macro allows specifying an access level for the generated mock. This can be useful when defining the visibility of mocks in your test suite or modules. The following access levels are supported:
61
+
62
+
- `open`: The most permissive access level, allowing subclassing and usage in other modules.
63
+
- `public`: Allows usage by other modules but restricts subclassing to within the defining module.
64
+
- `package`: Limits access to declarations within the same package, suitable for managing code visibility within related modules.
65
+
- `internal`: The default access level in Swift, exposing the mock only within the same module.
66
+
- `fileprivate`: Restricts access to the file where the mock is defined.
67
+
- `private`: Restricts access to the enclosing declaration, providing the highest level of encapsulation.
68
+
69
+
To specify an access level, provide it as a parameter to the macro:
70
+
71
+
```swift
72
+
@Mocked(.public)
73
+
protocol MyProtocol {
74
+
// Protocol requirements
75
+
}
76
+
```
77
+
78
+
## Edge Cases and Warnings
79
+
80
+
- **Non-Protocol Usage**: The `@Mocked` macro can only be applied to protocol definitions. Applying it to other types, such as classes or structs, will result in a compilation error.
81
+
- **Unimplemented Methods**: Any method that is not explicitly overridden will call `fatalError()` when invoked, ensuring that developers are alerted if a method is called without being properly mocked. Always ensure that all necessary methods are mocked to avoid runtime crashes.
82
+
- **Value vs. Reference Semantics**: The generated mock defaults to being a struct, which means it follows value semantics. If the protocol requires reference semantics (e.g., it conforms to `AnyObject`), the macro will generate a class instead.
83
+
84
+
## Best Practices
85
+
86
+
- **Keep Protocols Small and Focused**: Define small, focused protocols that capture a specific piece of functionality. This makes the generated mocks easier to use and understand.
87
+
- **Avoid Over-Mocking**: Mock only the behavior required for the test. Over-mocking can lead to brittle tests that are difficult to maintain.
88
+
- **Use Closures Thoughtfully**: When providing closures to override protocol methods, simulate realistic behaviors to create meaningful tests. For example, introduce delays for `async` methods or specific error types for `throws` methods.
65
89
66
-
# Advanced Usage
67
-
The `Mocked` macro can be used with more complex protocols, including those with associated types, `async` methods, `throws` methods, or a combination of both. This allows developers to test various scenarios, such as successful asynchronous operations or handling errors, without needing to write dedicated mock classes manually.
90
+
## Advanced Usage
91
+
92
+
The `Mocked` macro can handle more complex protocols, including those with associated types, `async` methods, `throws` methods, or combinations of these. This makes it easy to test scenarios involving asynchronous operations, error handling, or protocols with type constraints.
68
93
69
94
```swift
70
95
@Mocked
71
96
protocol ComplexProtocol {
72
97
associatedtype ItemType
73
-
associatedtype ItemValue: Codable
74
98
func fetchData() async throws -> ItemType
75
99
func processData(input: Int) -> Bool
76
-
func storeValue(value: ItemValue) -> Void
77
100
}
78
101
79
-
let mock = MockedComplexProtocol<String, Int>(
102
+
let mock = MockedComplexProtocol<String>(
80
103
fetchData: { return "Mocked Data" },
81
104
processData: { input in return input > 0 }
82
105
)
83
-
84
-
// Usage in a test
85
-
Task {
86
-
do {
87
-
let data = try await mock.fetchData()
88
-
print(data) // Output: "Mocked Data"
89
-
} catch {
90
-
XCTFail("Unexpected error: \(error)")
91
-
}
92
-
}
93
-
94
-
let isValid = mock.processData(input: 5)
95
-
XCTAssertTrue(isValid)
96
106
```
97
107
98
-
# Limitations
99
-
- **Associated Types**: The `@Mocked` macro currently supports protocols with associated types using generics. However, there may be scenarios where creating a type-erased wrapper could be beneficial, especially for protocols with complex associated type relationships.
100
-
- **Protocol Inheritance**: When mocking protocols that inherit from other protocols, the `@Mocked` macro will not automatically generate parent mocks for child protocols. Instead, extend the parent protocols or the child protocol to provide the necessary values or functions to conform to the inherited requirements.
108
+
In this example, `MockedComplexProtocol` provides custom behavior for `fetchData` and `processData`, allowing precise control over how the protocol's methods behave in your tests.
109
+
110
+
## Limitations
101
111
102
-
# Best Practices
103
-
- **Define Clear Protocols**: Define small, focused protocols that capture a specific piece of functionality. This makes the generated mocks easier to use and understand.
104
-
- **Avoid Over-Mocking**: Avoid mocking too much behavior in a single test, as it can lead to brittle tests that are difficult to maintain. Instead, focus on the specific interactions you want to verify.
105
-
- **Use Closures Thoughtfully**: Provide closures that simulate realistic behavior to make your tests more meaningful. For example, simulate network delays with `async` closures or return specific error types to test error handling paths.
112
+
- **Associated Types**: While the `Mocked` macro supports protocols with associated types using generics, there may be scenarios where creating a type-erased wrapper could be beneficial, especially for protocols with complex relationships between associated types.
113
+
- **Protocol Inheritance**: The `Mocked` macro will not automatically generate parent mocks for child protocols that inherit other protocols. Developers need to extend the generated mock to meet the requirements of inherited protocols manually.
106
114
*/
107
115
@attached(peer, names:prefixed(Mocked))
108
-
public macro Mocked()= #externalMacro(
116
+
public macro Mocked(_ accessLevel:AccessLevel=.internal)= #externalMacro(
0 commit comments