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

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

Tatsuya Tanaka
December 18, 2018

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

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 ͱͷ૊Έ߹Θ͕͓ͤ͢͢Ί