Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kateinoigakukun/stubkit
A smart stubbing system.
https://github.com/kateinoigakukun/stubkit
swift testing
Last synced: 6 days 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 (almost 6 years ago)
- Default Branch: main
- Last Pushed: 2024-08-07T17:49:45.000Z (5 months ago)
- Last Synced: 2024-10-11T12:24:27.478Z (3 months 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
![Run unit tests](https://github.com/kateinoigakukun/StubKit/workflows/Run%20unit%20tests/badge.svg) [![codecov](https://codecov.io/gh/kateinoigakukun/StubKit/branch/main/graph/badge.svg?token=WqSnBYoNJq)](https://codecov.io/gh/kateinoigakukun/StubKit)
![](https://img.shields.io/badge/platform-iOS%20%7C%20macOS%20%7C%20Linux%20%7C%20WebAssembly-blue)
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
![](./resources/tree.png)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.