Last active
March 29, 2022 13:06
-
-
Save IanKeen/d3a22473a8f946bffce213a16e02dc2f to your computer and use it in GitHub Desktop.
Custom Decoder that can be used to create Decodable instances that are populated with random values
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
import Foundation | |
extension Decodable { | |
public static func randomInstance() throws -> Self { | |
let decoder = RandomDecoder() | |
return try Self(from: decoder) | |
} | |
} | |
private class RandomDecoder: Decoder { | |
let codingPath: [CodingKey] = [] | |
let userInfo: [CodingUserInfoKey: Any] = [:] | |
init() { } | |
func container<Key: CodingKey>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> { | |
return .init(KeyedContainer<Key>()) | |
} | |
func unkeyedContainer() throws -> UnkeyedDecodingContainer { | |
return UnkeyedContainer() | |
} | |
func singleValueContainer() throws -> SingleValueDecodingContainer { | |
return SingleValueContainer() | |
} | |
struct KeyedContainer<Key: CodingKey>: KeyedDecodingContainerProtocol { | |
let allKeys: [Key] = [] | |
let codingPath: [CodingKey] = [] | |
init() { } | |
func contains(_ key: Key) -> Bool { | |
return true | |
} | |
func decodeNil(forKey key: Key) throws -> Bool { | |
return Bool.random() | |
} | |
func decode<T: Decodable>(_ type: T.Type, forKey key: Key) throws -> T { | |
return try T.randomInstance() | |
} | |
func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> { | |
return .init(KeyedContainer<NestedKey>()) | |
} | |
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { | |
return UnkeyedContainer() | |
} | |
func superDecoder() throws -> Decoder { | |
return RandomDecoder() | |
} | |
func superDecoder(forKey key: Key) throws -> Decoder { | |
return RandomDecoder() | |
} | |
} | |
struct UnkeyedContainer: UnkeyedDecodingContainer { | |
let codingPath: [CodingKey] = [] | |
let count: Int? = (0...5).randomElement() | |
var isAtEnd: Bool { return currentIndex == (count ?? 0) } | |
private(set) var currentIndex = 0 | |
init() { } | |
mutating func decodeNil() throws -> Bool { | |
return Bool.random() | |
} | |
mutating func decode<T: Decodable>(_ type: T.Type) throws -> T { | |
defer { currentIndex += 1 } | |
return try T.randomInstance() | |
} | |
mutating func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> { | |
return .init(KeyedContainer<NestedKey>()) | |
} | |
mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { | |
return UnkeyedContainer() | |
} | |
mutating func superDecoder() throws -> Decoder { | |
return RandomDecoder() | |
} | |
} | |
struct SingleValueContainer: SingleValueDecodingContainer { | |
let codingPath: [CodingKey] = [] | |
init() { } | |
func decodeNil() -> Bool { return Bool.random() } | |
func decode(_ type: Bool.Type) throws -> Bool { return Bool.random() } | |
func decode(_ type: String.Type) throws -> String { return String.random(length: Int.random(in: 0...50)) } | |
func decode(_ type: Double.Type) throws -> Double { return Double.random(in: 0...500) } | |
func decode(_ type: Float.Type) throws -> Float { return Float.random(in: 0...500) } | |
func decode(_ type: Int.Type) throws -> Int { return Int.random(in: 0...500) } | |
func decode(_ type: Int8.Type) throws -> Int8 { return Int8.random(in: 0...100) } | |
func decode(_ type: Int16.Type) throws -> Int16 { return Int16.random(in: 0...500) } | |
func decode(_ type: Int32.Type) throws -> Int32 { return Int32.random(in: 0...500) } | |
func decode(_ type: Int64.Type) throws -> Int64 { return Int64.random(in: 0...500) } | |
func decode(_ type: UInt.Type) throws -> UInt { return UInt.random(in: 0...500) } | |
func decode(_ type: UInt8.Type) throws -> UInt8 { return UInt8.random(in: 0...100) } | |
func decode(_ type: UInt16.Type) throws -> UInt16 { return UInt16.random(in: 0...500) } | |
func decode(_ type: UInt32.Type) throws -> UInt32 { return UInt32.random(in: 0...500) } | |
func decode(_ type: UInt64.Type) throws -> UInt64 { return UInt64.random(in: 0...500) } | |
func decode<T: Decodable>(_ type: T.Type) throws -> T { | |
return try T.randomInstance() | |
} | |
} | |
} | |
extension String { | |
private static let source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" | |
static func random<G: RandomNumberGenerator>(length: Int, using generator: inout G) -> String { | |
let range = 0...length | |
return range | |
.compactMap { _ in String.source.randomElement() } | |
.map(String.init) | |
.joined() | |
} | |
static func random(length: Int) -> String { | |
var g = SystemRandomNumberGenerator() | |
return String.random(length: length, using: &g) | |
} | |
} |
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
struct User: Decodable { | |
let string: String | |
let optional: String? | |
let integer: Int | |
let boolean: Bool | |
let array: [Int] | |
let nested: Pet | |
} | |
struct Pet: Decodable { | |
let name: String | |
} | |
let u = try User.randomInstance() | |
/* Example Output | |
User(string: "maeDILfJkgnoxubFJQZpb", optional: nil, integer: 94, boolean: true, array: [34], nested: __lldb_expr_78.Pet(name: "EwZolDJsseaLaahYCvdjy")) | |
User(string: "DzVphWEYBWHSAhuRrwvYN", optional: Optional("sKmTPmlbdBIgExiFnRmGl"), integer: 397, boolean: false, array: [], nested: __lldb_expr_78.Pet(name: "aMqWeAaPKMszPrLClqBNP")) | |
User(string: "GNOoLQLwCdGASCpcIijhh", optional: nil, integer: 237, boolean: true, array: [179, 76], nested: __lldb_expr_78.Pet(name: "HzHPqpscGwxxqrRdteaYi")) | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment