-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSafeCodableTests.swift
More file actions
248 lines (213 loc) · 7.14 KB
/
Copy pathSafeCodableTests.swift
File metadata and controls
248 lines (213 loc) · 7.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
//
// SafeCodableTests.swift
// SafeCodableTests
//
// Created by Xiaobo Nie on 2024/5/18.
//
import XCTest
@testable import SafeCodable
final class SafeCodableTests: XCTestCase {
typealias S = Safety // 非空
typealias O = Option // 可选
struct TestResponseModel: Codable {
@S.type var code: Int
@S.type var message: String
// model
@O.model<TestPersonInfo> var student
// 模型数组
@S.array<TestPersonInfo> var values
@O.array<TestPersonInfo> var infos
// Int数组
@O.array<Int> var ids
// 枚举 String
@S.enums<Sex> var sex
// 枚举 Int
@O.enums<Sex2> var sex2
// 字典 对应json 中 dic_a 和 dic_b
@S.dict<String, AnyCodable> var dicA
@S.dict<Int, String> var dicB
// 元祖不支持直接解析,使用方法实现
@O.string var street
@O.string var city
func address() -> (street: String?, city: String?) {
(street, city)
}
}
struct TestPersonInfo : Codable {
// 使用统一风格
@O.type var uid: Int?
// 使用特定类型
@O.int var type
@O.string var country
@O.bool var close
// 使用统一风格
@S.type var suc: Bool
// 使用特定类型
@S.string var name
@S.int var age
@S.bool var like
// 设置默认值,对象init构建时生效,解析不生效
@S.string var weightUnit = "kg"
@S.float var weight = 55
// 指定其他key进行解析(uid->id) ,尽量使用原始key,除非涉及关键词
// 属性编辑完成后,通过方法补全设置CodingKeys, 后续增删都需要对应修改,不在CodingKeys范围内的均不会参与解析
enum CodingKeys: String, CodingKey {
case uid = "id"
case type
case country
case close
case suc
case name
case age
case like
case weightUnit
case weight
}
}
enum Sex: String, RawDefaultCodable {
static func defaultValue() -> Sex {
.unknown
}
case unknown
case male
case female
}
enum Sex2: Int, RawDefaultCodable {
static func defaultValue() -> Sex2 {
.unknown
}
case unknown = 0
case male = 1
case female = 2
}
var result: TestResponseModel?
var resultNew: TestResponseModel?
let resDic = """
{
"id": 2,
"code": "200",
"values": [
{
"id": 3.0,
"type": 4.0,
"country": "China",
"suc": true
},
{
"id": "9.0",
"type": "8",
"country": "USA",
"suc": 0
}
],
"ids": [
1,
"2"
],
"dic_a": {
"id": 1,
"name": {
"id": 1,
"name": "std 1"
}
},
"dic_b": {
"1": 1,
"2": "std 1",
"3": "std 2"
},
"students": [
{
"id": 2,
"name": "std 2"
},
{
"id": 3,
"name": "std 3"
}
],
"student": {
"id": 1,
"name": "std 1"
},
"street": "Longyang load.",
"city": "ShangHai",
"sex": "male",
"sex2": "1"
}
"""
override func setUpWithError() throws {
result = nil
}
override func tearDownWithError() throws {
check(result: self.result, tip: "原始json数据解码")
check(result: self.resultNew, tip: "编码后json数据解码")
func check(result: TestResponseModel?, tip: String) {
guard let result else {
XCTFail(tip + " - 解析失败")
return
}
XCTAssertEqual(result.code, 200, "类型不匹配解析失败")
XCTAssertEqual(result.message, "", "String: key空设置默认值失败")
XCTAssert(result.values.count > 0, "Array: 列表解析失败")
// 空Array
XCTAssertEqual(result.infos?.count, nil, "Array: 可选&空失败")
XCTAssert(result.values.count == 2, "Array: 列表解析缺失")
for (index, model) in result.values.enumerated() {
switch index {
case 0:
XCTAssert(model.suc, "Bool: <- true 解析失败")
XCTAssertEqual(model.type, 4, "Int <- 4.0 解析失败")
XCTAssertEqual(model.country, "China", "String 解析失败")
break
case 1:
XCTAssert(!model.suc, "Bool: <- 0 解析失败")
XCTAssertEqual(model.type, 8, "Int <- \"8\" 解析失败")
break
default:
break
}
}
XCTAssert(result.ids?.count ?? 0 > 0, "ids: 列表解析缺失")
XCTAssert(result.student != nil, "model: 解析缺失")
XCTAssert(result.sex == .male, "enum: 解析缺失")
XCTAssert(result.sex2 == .male, "enum: 解析缺失")
XCTAssert(result.dicA.count > 0, "[String: AnyCodable]: 解析失败")
XCTAssert(result.dicB.count > 0, "[Int: String]: 解析失败")
}
}
func testExample() throws {
do {
let jsonData = resDic.data(using: .utf8)
if let jsonData {
let decoder = JSONDecoder()
// 处理驼峰命名法的属性
decoder.keyDecodingStrategy = .convertFromSnakeCase
// 原始json to model 解码
result = try decoder.decode(TestResponseModel.self, from: jsonData)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do {
let jsonData = try encoder.encode(result)
// model to json 编码
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
// 编码后json to model 解码
resultNew = try decoder.decode(TestResponseModel.self, from: jsonData)
dump(resultNew)
}
} catch {
XCTFail("Encoding failed: \(error.localizedDescription)")
}
}
} catch {
XCTFail("Error decoding JSON: \(error)")
}
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}