https://github.com/kateinoigakukun/stubkit
A smart stubbing system.
https://github.com/kateinoigakukun/stubkit
swift testing
Last synced: over 1 year ago
JSON representation
A smart stubbing system.
- Host: GitHub
- URL: https://github.com/kateinoigakukun/stubkit
- Owner: kateinoigakukun
- License: mit
- Created: 2018-12-30T02:18:10.000Z (over 7 years ago)
- Default Branch: main
- Last Pushed: 2024-08-07T17:49:45.000Z (almost 2 years ago)
- Last Synced: 2024-10-11T12:24:27.478Z (almost 2 years ago)
- Topics: swift, testing
- Language: Swift
- Homepage:
- Size: 188 KB
- Stars: 299
- Watchers: 11
- Forks: 10
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# StubKit
 [](https://codecov.io/gh/kateinoigakukun/StubKit)

A smart stubbing system.
## Installation
### Using Swift Package Manager
```
.package(url: "https://github.com/kateinoigakukun/StubKit.git", from: "0.1.0"),
```
### Using [Carthage](https://github.com/Carthage/Carthage)
```
github "kateinoigakukun/StubKit"
```
### Using [CocoaPods](https://cocoapods.org/)
```
pod "SwiftStubKit"
```
## Usage
### Getting started
You can instantiate any kind of `Decodable` with a single line.
```swift
import StubKit
// Codable struct
struct User: Codable {
let id: Int
let name: String
let sex: Sex
}
let stubUser = try Stub.make(User.self)
// User(id: 1234, name: "This is Stub String", sex: .female)
```
### Customize property
You can customize properties even if the property is defined as `let`.
```swift
let maleUser = try Stub.make(User.self) {
$0.set(\.sex, value: .male)
}
// User(id: 1234, name: "This is Stub String", sex: .male)
```
### Using `Stubbable`
If you want to customize the default stub value, please conform `Stubbable`.
```swift
extension String: Stubbable {
static func stub() -> String {
return "This is custommized Stub String"
}
}
let stubUser = try Stub.make(User.self)
// User(id: 1234, name: "This is customized Stub String", sex: .female)
```
### Advanced Usage
```swift
struct RandomIntStubProvider: StubProvider {
func stub(of type: T.Type) -> T? {
if type is Int.Type {
return Int.random(in: 0..<100) as? T
}
return nil
}
}
let userStub = Stub(type: User.self, provider: [RandomIntStubProvider()])
try userStub.make() // User(id: 97)
try userStub.make() // User(id: 54)
try userStub.make() // User(id: 12)
```
### Need to conform non-final class as Stubbable?
You can make it `Stubbable` by defining the `UnsafeStubbable`.
```swift
public protocol UnsafeStubbable: Stubbable {
associatedtype Target = Self
static func unsafeStub() -> Target
}
extension UnsafeStubbable {
public static func stub() -> Self {
return unsafeStub() as! Self
}
}
extension UIImage: UnsafeStubbable {
public static func unsafeStub() -> UIImage {
return #imageLiteral(resourceName: "dummy")
}
}
```
## How does it work
StubKit mainly uses two techniques.
- Traverse using `Decoder` protocol.
- Inject value with non-mutable `KeyPath`.
- Existential type using `Self`.
### Traverse struct using `Decoder` protocol

Swift has `Decodable` protocol and if a type conforms to `Decodable`, Swift compiler generates some code to decode internally. So we can decode a JSON to Swift struct without any configuration. StubKit uses this system to construct instance through `Decoder`. `Decoder` is a protocol which provide a value by key or index like `JSONDecoder`. If we pass the `Decoder` which just provide a stub value recursively, we can instantiate any kind of `Decodable` instance.
### Inject value with non-mutable `KeyPath`
I know it's only natural but, Swift can't mutate `let` defined property. But Swift has `MemoryLayout.offset` which provide the offset to the property from its own address. So actually in memory we can mutate `let` property.
### Existential type using `Self`
A protocol that has `associatedtype` or uses `Self` type can't be existential type. But using `Self` for return type of method is only available in Swift4.2. (I think using `Self` for type of getter should be also available.) This technique makes `Stubbale` type-safely.