Upgrade to Pro — share decks privately, control downloads, hide ads and more …

テストデータの自動生成方法の別解

Avatar for Tatsuya Tanaka Tatsuya Tanaka
December 18, 2018

 テストデータの自動生成方法の別解

Avatar for Tatsuya Tanaka

Tatsuya Tanaka

December 18, 2018
Tweet

More Decks by Tatsuya Tanaka

Other Decks in Technology

Transcript

  1. ViewModelͷςετ self.viewModel = ViewModel( trainStatusInfoAPI: self.trainStatusInfoAPI, registrationAPI: self.registrationAPI, congestionAPI: self.congestionAPI

    ) self.trainStatusInfoAPI.mock = TestData(...) self.registrationAPI.mock = TestData2(...) self.congestionAPI.mock = TestData3(...) self.viewModel.request() expect(self.viewModel.cellData.value).toEventually(equal(...)) ←͕͜͜໘౗
  2. ςετσʔλͷॳظԽ TrainStatusInfo(statuses: [ Status(railway: Railway(information: Information( code: 0, name: "",

    trainStatuses: [ TrainStatus( impactRange: "impactRange", message: "message", statusTitle: "statusTitle", statusCode: "statusCode", updatedAt: "updatedAt", …, ) ], displayName: "displayName", webURL: "webURL", fooCode: 0, barCode: 1, bazName: "bazName", quxName: "quxName", quuxID: 2, corge: “corge", grault: “grault", …, ) ) )], …: Hoge(…)) ͜Μͳͷॻ͖ͨ͘ͳ͍
 ਓྨͷ࢓ࣄ͡Όͳ͍ (´ɾωɾʆ)
  3. ཚ਺ͰॳظԽ͢ΔͨΊͷΞϓϩʔν • ✖ ίʔυੜ੒: ΞϦ͚ͩͲPure SwiftͰ΍Γ͍ͨ • ϦϑϨΫγϣϯ • ✖

    Mirror: ࢖͏લʹΠϯελϯεԽ͕ඞཁ (㱻) • Codable: ΠϯελϯεԽͳ͠Ͱ
 ܕͷߏ଄ΛಘΒΕΔ
  4. Randomizable struct A: Decodable, Randomizable { let string: String let

    int: Int let child: Child struct Child: Decodable, Randomizable { let url: URL let array: [Double] } } print(A.randomValue()) // A(string: "lgygox", int: Optional(7366465203208943133), child: Child(url: https://laoygisn.com, array: [4.67308519, 1.8293057, -6.64297, 7.797])) ↓ཚ਺ͰॳظԽͯ͘͠ΕΔ
  5. Randomizable struct ParentType: Decodable, Randomizable { let customType: CustomType struct

    CustomType: Decodable, Randomizable { let animal: String static func randomValue() -> A.Child.ChildChild { return .init(animal: ["", "", ""].randomElement()!) } } } ↓ϥϯμϜͷϧʔϧΛΧελϚΠζͰ͖Δ // ParentType(customType: CustomType(animal: "")) print(ParentType.randomValue())
  6. Randomizable public protocol Randomizable { static func randomValue() -> Self

    } public extension Randomizable where Self: Decodable { public static func randomValue() -> Self { let randomDecoder = RandomDecoder() return try! randomDecoder.decode(Self.self) } } Decodableʹconform͍ͯ͠Δ৔߹͸ϥϯμϜͷ࣮૷Λෆཁʹ
  7. Randomizableͷඪ४αϙʔτ extension Bool: Randomizable {} extension Int: Randomizable {} extension

    Int8: Randomizable {} extension Int16: Randomizable {} extension Int32: Randomizable {} extension Int64: Randomizable {} extension UInt: Randomizable {} extension UInt8: Randomizable {} extension UInt16: Randomizable {} extension UInt32: Randomizable {} extension UInt64: Randomizable {} extension Float: Randomizable {} extension Double: Randomizable {} extension String: Randomizable {} extension Optional: Randomizable where Wrapped: Randomizable { public static func randomValue() -> Optional<Wrapped> { return Bool.random() ? nil : Wrapped.randomValue() } } extension URL: Randomizable { public static func randomValue() -> URL { return URL(string: "https://\(String.defaultRandom()).com")! } }
  8. RandomDecoderͷ࣮૷ import Foundation open class RandomDecoder: Decoder { open var

    codingPath: [CodingKey] open var userInfo: [CodingUserInfoKey: Any] = [:] public init(codingPath: [CodingKey] = []) { self.codingPath = codingPath } open func container<Key: CodingKey>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> { return KeyedDecodingContainer(KeyedContainer<Key>(decoder: self, codingPath: [])) } open func unkeyedContainer() throws -> UnkeyedDecodingContainer { return UnkeyedContanier(decoder: self) } open func singleValueContainer() throws -> SingleValueDecodingContainer { return SingleValueContanier(decoder: self) } func random<T: Decodable>() throws -> T { if let randomType = T.self as? Randomizable.Type { return randomType.randomValue() as! T } else { return try T(from: self) } } } extension RandomDecoder { open func decode<T : Decodable>(_ type: T.Type) throws -> T { return try T(from: self) } } extension RandomDecoder { private class KeyedContainer<Key: CodingKey>: KeyedDecodingContainerProtocol { private var decoder: RandomDecoder private(set) var codingPath: [CodingKey] init(decoder: RandomDecoder, codingPath: [CodingKey]) { self.decoder = decoder self.codingPath = codingPath } var allKeys: [Key] { return [] } func contains(_ key: Key) -> Bool { return true } func decodeNil(forKey key: Key) throws -> Bool { return Bool.random() } func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { return .random() } func decode(_ type: Int.Type, forKey key: Key) throws -> Int { return .defaultRamdom() } func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { return .defaultRamdom() } func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { return .defaultRamdom() } func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { return .defaultRamdom() } func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { return .defaultRamdom() } func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { return .defaultRamdom() } func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { return .defaultRamdom() } func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { return .defaultRamdom() } func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { return .defaultRamdom() } func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { return .defaultRamdom() } func decode(_ type: Float.Type, forKey key: Key) throws -> Float { return .defaultRamdom() } func decode(_ type: Double.Type, forKey key: Key) throws -> Double { return .defaultRamdom() } func decode(_ type: String.Type, forKey key: Key) throws -> String { return .defaultRamdom() } func decode<T: Decodable>(_ type: T.Type, forKey key: Key) throws -> T { decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } return try decoder.random() } func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey { decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } return KeyedDecodingContainer(KeyedContainer<NestedKey>(decoder: decoder, codingPath: [])) } func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } return UnkeyedContanier(decoder: decoder) } func _superDecoder(forKey key: CodingKey = AnyCodingKey.super) throws -> Decoder { decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } return RandomDecoder() } func superDecoder() throws -> Decoder { return try _superDecoder() } func superDecoder(forKey key: Key) throws -> Decoder { return try _superDecoder(forKey: key) } } private class UnkeyedContanier: UnkeyedDecodingContainer { private var decoder: RandomDecoder private(set) var codingPath: [CodingKey] private(set) var count: Int? var isAtEnd: Bool { return currentIndex >= count! } private(set) var currentIndex: Int = 0 private var currentCodingPath: [CodingKey] { return decoder.codingPath + [AnyCodingKey(index: currentIndex)] } init(decoder: RandomDecoder) { self.decoder = decoder self.codingPath = decoder.codingPath count = Int.random(in: 2...4) } func ramdom<T: DefaultRamdom>(_ type: T.Type) throws -> T { decoder.codingPath.append(AnyCodingKey(index: currentIndex)) defer { decoder.codingPath.removeLast() currentIndex += 1 } return T.defaultRamdom() } func decodeNil() throws -> Bool { return false } func decode(_ type: Bool.Type) throws -> Bool { return try ramdom(type) } func decode(_ type: Int.Type) throws -> Int { return try ramdom(type) } func decode(_ type: Int8.Type) throws -> Int8 { return try ramdom(type) } func decode(_ type: Int16.Type) throws -> Int16 { return try ramdom(type) } func decode(_ type: Int32.Type) throws -> Int32 { return try ramdom(type) } func decode(_ type: Int64.Type) throws -> Int64 { return try ramdom(type) } func decode(_ type: UInt.Type) throws -> UInt { return try ramdom(type) } func decode(_ type: UInt8.Type) throws -> UInt8 { return try ramdom(type) } func decode(_ type: UInt16.Type) throws -> UInt16 { return try ramdom(type) } func decode(_ type: UInt32.Type) throws -> UInt32 { return try ramdom(type) } func decode(_ type: UInt64.Type) throws -> UInt64 { return try ramdom(type) } func decode(_ type: Float.Type) throws -> Float { return try ramdom(type) } func decode(_ type: Double.Type) throws -> Double { return try ramdom(type) } func decode(_ type: String.Type) throws -> String { return try ramdom(type) } func decode<T: Decodable>(_ type: T.Type) throws -> T { decoder.codingPath.append(AnyCodingKey(index: currentIndex)) defer { decoder.codingPath.removeLast() currentIndex += 1 } return try decoder.random() } func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> { decoder.codingPath.append(AnyCodingKey(index: currentIndex)) defer { decoder.codingPath.removeLast() currentIndex += 1 } return KeyedDecodingContainer(KeyedContainer<NestedKey>(decoder: decoder, codingPath: [])) } func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { decoder.codingPath.append(AnyCodingKey(index: currentIndex)) defer { decoder.codingPath.removeLast() currentIndex += 1 } return UnkeyedContanier(decoder: decoder) } func superDecoder() throws -> Decoder { decoder.codingPath.append(AnyCodingKey(index: currentIndex)) defer { decoder.codingPath.removeLast() currentIndex += 1 } return RandomDecoder(codingPath: decoder.codingPath) } } private class SingleValueContanier: SingleValueDecodingContainer { private var decoder: RandomDecoder var codingPath: [CodingKey] { return decoder.codingPath } init(decoder: RandomDecoder) { self.decoder = decoder } func decodeNil() -> Bool { return true } func decode(_ type: Bool.Type) throws -> Bool { return .defaultRamdom() } func decode(_ type: Int.Type) throws -> Int { return .defaultRamdom() } func decode(_ type: Int8.Type) throws -> Int8 { return .defaultRamdom() } func decode(_ type: Int16.Type) throws -> Int16 { return .defaultRamdom() } func decode(_ type: Int32.Type) throws -> Int32 { return .defaultRamdom() } func decode(_ type: Int64.Type) throws -> Int64 { return .defaultRamdom() } func decode(_ type: UInt.Type) throws -> UInt { return .defaultRamdom() } func decode(_ type: UInt8.Type) throws -> UInt8 { return .defaultRamdom() } func decode(_ type: UInt16.Type) throws -> UInt16 { return .defaultRamdom() } func decode(_ type: UInt32.Type) throws -> UInt32 { return .defaultRamdom() } func decode(_ type: UInt64.Type) throws -> UInt64 { return .defaultRamdom() } func decode(_ type: Float.Type) throws -> Float { return .defaultRamdom() } func decode(_ type: Double.Type) throws -> Double { return .defaultRamdom() } func decode(_ type: String.Type) throws -> String { return .defaultRamdom() } func decode<T: Decodable>(_ type: T.Type) throws -> T { return try decoder.random() } } }
  9. RandomDecoderͷ࣮૷ͷϐοΫΞοϓ extension RandomDecoder { private class KeyedContainer<Key: CodingKey>: KeyedDecodingContainerProtocol {

    private var decoder: RandomDecoder private(set) var codingPath: [CodingKey] init(decoder: RandomDecoder, codingPath: [CodingKey]) { self.decoder = decoder self.codingPath = codingPath } func decodeNil(forKey key: Key) throws -> Bool { return .random() } func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { return .random() } func decode(_ type: Int.Type, forKey key: Key) throws -> Int { return .defaultRandom() } func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { return .defaultRandom() } func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { return .defaultRandom() } func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { return .defaultRandom() } func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { return .defaultRandom() } func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { return .defaultRandom() } func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { return .defaultRandom() } func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { return .defaultRandom() } func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { return .defaultRandom() } func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { return .defaultRandom() } func decode(_ type: Float.Type, forKey key: Key) throws -> Float { return .defaultRandom() } func decode(_ type: Double.Type, forKey key: Key) throws -> Double { return .defaultRandom() } func decode(_ type: String.Type, forKey key: Key) throws -> String { return .defaultRandom() } func decode<T: Decodable>(_ type: T.Type, forKey key: Key) throws -> T { decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } return try decoder.random() } ͱʹ͔͘ཚ਺Λฦͯ͠Δ͚ͩ
  10. ͦͷଞ׆༻ํ๏ struct JSON: Codable, Randomizable { let value: String }

    let json = JSON.randomValue() let jsonData = try JSONEncoder().encode(json) let jsonString = String(data: jsonData, encoding: .utf8) ϥϯμϜͳJSONจࣈྻͷ࡞੒
  11. Swift Package ManagerରԠ ίϚϯυϥΠϯπʔϧͰ׆༻Ͱ͖ͦ͏ͳͷͰɺ
 Swift Package ManagerʹରԠ͠·ͨ͠ import PackageDescription let

    package = Package( name: "Hoge", dependencies: [ .package(url: "https://github.com/tattn/Randomizable.git", from: “1.0.3"), .package(url: "https://github.com/tattn/MoreCodable.git", from: “0.2.2") ], targets: [ .target( name: “Hoge", dependencies: ["Randomizable", "MoreCodable"]) ] ) IUUQTHJUIVCDPNUBUUO.PSF$PEBCMF ͱͷ૊Έ߹Θ͕͓ͤ͢͢Ί