單元測試學習筆記02_虛設常式(stub)、模擬物件(Mock)_call api為例


一般測試中比較容易頭疼就是在於對於外部資源會有依賴
(像是一段function涉及到發信、寫Log檔、讀取外部文件或者call web api, web service等)

而除了外部資源外還可能會有我只想驗證自己負責的class中特定方法但該方法可能又有去呼叫或者存取也就是依賴到其餘class的方法甚至層層相依等問題
如下圖所示

測試過程往往會遇到目標要測試的方法當中又相依很多其餘的class
甚至會往下相依多個層級

此時我們會藉由類似建構假物件(Fake)的方式來把關注點分離
進行隔離測試程式碼


有點類似
「各人自掃門前雪,莫管他人瓦上霜」的含意。



Microsoft Fakes 會以「虛設常式」或「填充碼」取代應用程式的其他部分,協助您隔離要測試的程式碼。 這些是受測試所控制的一些程式碼片段。 藉由隔離待測的程式碼,您可以在正確的位置尋找測試失敗的原因。 即使應用程式的其他部分還無法運作,您也可以利用虛設常式和填充碼。
該框架鎖定義Fakes 分為兩種類別:

虛設常式(stub)
會以一小段實作相同介面的類別取代類別。 若要使用虛設常式,您所設計的應用程式必須讓每個元件只相依於介面,而不相依於其他元件。 (「元件」表示一起設計及更新的類別或類別群組,通常會包含在組件中)。

填充碼(shim)
會在執行階段修改應用程式的編譯程式碼,以便執行您的測試所提供的填充碼,而不是進行指定的方法呼叫。 您可以使用填充碼取代您無法修改的組件 (例如 .NET 組件) 的呼叫。



不過很遺憾這功能只有在enterprice版本的visual studio才有支援

這裡我就沒特別用它來示範了


而一般Fake通用觀念就是上一篇提及到的
主要分成兩大類

虛設常式(stub)
通常用於驗證目標回傳值,以及驗證目標物件狀態的改變(State-Based Testing)
These are extremely simplified versions of Mocks, which typically just return a set of hard-coded values, and/or fail, again, in simplistic ways.


模擬物件(Mock)
用於驗證目標物件與外部相依介面的互動方式(Interaction-Based Testing)
(使用情境發生在於沒有回傳值的情況)
A full-on stand in for the dependency, that replicates most or all of the behavior that you actually give a s**t about. These can actually get somewhat hairy (hence the “Code Smell” crack above), since, in extremis, you basically need to track/replicate the functionality of the dependency.



這裡我模擬一個call web api 查找出新竹縣長照機構位置資料的需求








這裡copy一份API回傳結果以免後續存取不到的人不好模擬

  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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
[
  {
    "編號": "1",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹北",
    "機構名稱": "新竹縣私立安歆綜合式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市四維街228號",
    "電話": "03-5536679"
  },
  {
    "編號": "2",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹北",
    "機構名稱": "社團法人中華民國紅十字會台灣省新竹縣支會附設新竹縣私立博愛居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市新泰路92號7樓之1",
    "電話": "03-5535850"
  },
  {
    "編號": "3",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹北",
    "機構名稱": "社團法人中華民國五福社會服務協會附設新竹縣私立五福居家式服務類長期照顧服務機構",
    "郵遞區號": "304",
    "地址": "新竹縣新豐鄉莊敬街26巷1弄15號",
    "電話": "03-5576556"
  },
  {
    "編號": "4",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹北",
    "機構名稱": "信馨居服有限公司附設新竹縣私立信馨居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市中泰路6號2樓",
    "電話": "03-5528207"
  },
  {
    "編號": "5",
    "服務類型": "居家服務",
    "特約鄉鎮": "新豐",
    "機構名稱": "新竹縣私立安歆綜合式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市四維街228號",
    "電話": "03-5536679"
  },
  {
    "編號": "6",
    "服務類型": "居家服務",
    "特約鄉鎮": "新豐",
    "機構名稱": "社團法人中華民國五福社會服務協會附設新竹縣私立五福居家式服務類長期照顧服務機構",
    "郵遞區號": "304",
    "地址": "新竹縣新豐鄉莊敬街26巷1弄15號",
    "電話": "03-5576556"
  },
  {
    "編號": "7",
    "服務類型": "居家服務",
    "特約鄉鎮": "新豐",
    "機構名稱": "社團法人中華民國紅十字會台灣省新竹縣支會附設新竹縣私立博愛居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市新泰路92號7樓之1",
    "電話": "03-5535850"
  },
  {
    "編號": "8",
    "服務類型": "居家服務",
    "特約鄉鎮": "新豐",
    "機構名稱": "信馨居服有限公司附設新竹縣私立信馨居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市中泰路6號2樓",
    "電話": "03-5528207"
  },
  {
    "編號": "9",
    "服務類型": "居家服務",
    "特約鄉鎮": "湖口",
    "機構名稱": "社團法人中華民國五福社會服務協會附設新竹縣私立五福居家式服務類長期照顧服務機構",
    "郵遞區號": "304",
    "地址": "新竹縣新豐鄉莊敬街26巷1弄15號",
    "電話": "03-5576556"
  },
  {
    "編號": "10",
    "服務類型": "居家服務",
    "特約鄉鎮": "湖口",
    "機構名稱": "社團法人中華民國紅十字會台灣省新竹縣支會附設新竹縣私立博愛居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市新泰路92號7樓之1",
    "電話": "03-5535850"
  },
  {
    "編號": "11",
    "服務類型": "居家服務",
    "特約鄉鎮": "湖口",
    "機構名稱": "新竹縣私立安歆綜合式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市四維街228號",
    "電話": "03-5536679"
  },
  {
    "編號": "12",
    "服務類型": "居家服務",
    "特約鄉鎮": "湖口",
    "機構名稱": "信馨居服有限公司附設新竹縣私立信馨居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市中泰路6號2樓",
    "電話": "03-5528207"
  },
  {
    "編號": "13",
    "服務類型": "居家服務",
    "特約鄉鎮": "湖口",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "14",
    "服務類型": "居家服務",
    "特約鄉鎮": "寶山",
    "機構名稱": "新竹縣私立安歆綜合式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市四維街228號",
    "電話": "03-5536679"
  },
  {
    "編號": "15",
    "服務類型": "居家服務",
    "特約鄉鎮": "寶山",
    "機構名稱": "社團法人中華民國五福社會服務協會附設新竹縣私立五福居家式服務類長期照顧服務機構",
    "郵遞區號": "304",
    "地址": "新竹縣新豐鄉莊敬街26巷1弄15號",
    "電話": "03-5576556"
  },
  {
    "編號": "16",
    "服務類型": "居家服務",
    "特約鄉鎮": "寶山",
    "機構名稱": "社團法人中華民國紅十字會台灣省新竹縣支會附設新竹縣私立博愛居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市新泰路92號7樓之1",
    "電話": "03-5535850"
  },
  {
    "編號": "17",
    "服務類型": "居家服務",
    "特約鄉鎮": "寶山",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "18",
    "服務類型": "居家服務",
    "特約鄉鎮": "新埔",
    "機構名稱": "新竹縣私立禾意關懷協會居家式服務類長期照顧服務機構",
    "郵遞區號": "305",
    "地址": "新竹縣新埔鎮四座里楊新路一段5號",
    "電話": "03-5886158"
  },
  {
    "編號": "19",
    "服務類型": "居家服務",
    "特約鄉鎮": "新埔",
    "機構名稱": "社團法人中華民國紅十字會台灣省新竹縣支會附設新竹縣私立博愛居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市新泰路92號7樓之1",
    "電話": "03-5535850"
  },
  {
    "編號": "20",
    "服務類型": "居家服務",
    "特約鄉鎮": "新埔",
    "機構名稱": "財團法人台灣省天主教會新竹教區附設新竹縣私立長安老人養護中心",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮長安路130號",
    "電話": "03-5943987"
  },
  {
    "編號": "21",
    "服務類型": "居家服務",
    "特約鄉鎮": "新埔",
    "機構名稱": "新竹縣私立安歆綜合式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市四維街228號",
    "電話": "03-5536679"
  },
  {
    "編號": "22",
    "服務類型": "居家服務",
    "特約鄉鎮": "新埔",
    "機構名稱": "信馨居服有限公司附設新竹縣私立信馨居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市中泰路6號2樓",
    "電話": "03-5528207"
  },
  {
    "編號": "23",
    "服務類型": "居家服務",
    "特約鄉鎮": "關西",
    "機構名稱": "新竹縣私立禾意關懷協會居家式服務類長期照顧服務機構",
    "郵遞區號": "305",
    "地址": "新竹縣新埔鎮四座里楊新路一段5號",
    "電話": "03-5886158"
  },
  {
    "編號": "24",
    "服務類型": "居家服務",
    "特約鄉鎮": "關西",
    "機構名稱": "社團法人中華民國紅十字會台灣省新竹縣支會附設新竹縣私立博愛居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市新泰路92號7樓之1",
    "電話": "03-5535850"
  },
  {
    "編號": "25",
    "服務類型": "居家服務",
    "特約鄉鎮": "關西",
    "機構名稱": "財團法人台灣省天主教會新竹教區附設新竹縣私立長安老人養護中心",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮長安路130號",
    "電話": "03-5943987"
  },
  {
    "編號": "26",
    "服務類型": "居家服務",
    "特約鄉鎮": "關西",
    "機構名稱": "新竹縣私立安歆綜合式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市四維街228號",
    "電話": "03-5536679"
  },
  {
    "編號": "27",
    "服務類型": "居家服務",
    "特約鄉鎮": "關西",
    "機構名稱": "信馨居服有限公司附設新竹縣私立信馨居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市中泰路6號2樓",
    "電話": "03-5528207"
  },
  {
    "編號": "28",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹東",
    "機構名稱": "財團法人台灣省天主教會新竹教區附設新竹縣私立長安老人養護中心",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮長安路130號",
    "電話": "03-5943987"
  },
  {
    "編號": "29",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹東",
    "機構名稱": "社團法人中華民國紅十字會台灣省新竹縣支會附設新竹縣私立博愛居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市新泰路92號7樓之1",
    "電話": "03-5535850"
  },
  {
    "編號": "30",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹東",
    "機構名稱": "社團法人中華民國原住民老人長期照顧暨婦幼受暴緊急安置發展關懷協會附設新竹縣私立拿互依居家長照機構",
    "郵遞區號": "313",
    "地址": "新竹縣尖石鄉嘉樂村2鄰麥樹仁5號",
    "電話": "03-5841968"
  },
  {
    "編號": "31",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹東",
    "機構名稱": "社團法人中華民國五福社會服務協會附設新竹縣私立五福居家式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮幸福路121巷2號9樓",
    "電話": "03-5960775"
  },
  {
    "編號": "32",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹東",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "33",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹東",
    "機構名稱": "新竹縣私立安歆綜合式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市四維街228號",
    "電話": "03-5536679"
  },
  {
    "編號": "34",
    "服務類型": "居家服務",
    "特約鄉鎮": "竹東",
    "機構名稱": "信馨居服有限公司附設新竹縣私立信馨居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市中泰路6號2樓",
    "電話": "03-5528207"
  },
  {
    "編號": "35",
    "服務類型": "居家服務",
    "特約鄉鎮": "芎林",
    "機構名稱": "財團法人台灣省天主教會新竹教區附設新竹縣私立長安老人養護中心",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮長安路130號",
    "電話": "03-5943987"
  },
  {
    "編號": "36",
    "服務類型": "居家服務",
    "特約鄉鎮": "芎林",
    "機構名稱": "社團法人中華民國紅十字會台灣省新竹縣支會附設新竹縣私立博愛居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市新泰路92號7樓之1",
    "電話": "03-5535850"
  },
  {
    "編號": "37",
    "服務類型": "居家服務",
    "特約鄉鎮": "芎林",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "38",
    "服務類型": "居家服務",
    "特約鄉鎮": "芎林",
    "機構名稱": "新竹縣私立安歆綜合式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市四維街228號",
    "電話": "03-5536679"
  },
  {
    "編號": "39",
    "服務類型": "居家服務",
    "特約鄉鎮": "芎林",
    "機構名稱": "信馨居服有限公司附設新竹縣私立信馨居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市中泰路6號2樓",
    "電話": "03-5528207"
  },
  {
    "編號": "40",
    "服務類型": "居家服務",
    "特約鄉鎮": "橫山",
    "機構名稱": "社團法人中華民國原住民老人長期照顧暨婦幼受暴緊急安置發展關懷協會附設新竹縣私立拿互依居家長照機構",
    "郵遞區號": "313",
    "地址": "新竹縣尖石鄉嘉樂村2鄰麥樹仁5號",
    "電話": "03-5841968"
  },
  {
    "編號": "41",
    "服務類型": "居家服務",
    "特約鄉鎮": "橫山",
    "機構名稱": "財團法人台灣省天主教會新竹教區附設新竹縣私立長安老人養護中心",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮長安路130號",
    "電話": "03-5943987"
  },
  {
    "編號": "42",
    "服務類型": "居家服務",
    "特約鄉鎮": "橫山",
    "機構名稱": "社團法人中華民國紅十字會台灣省新竹縣支會附設新竹縣私立博愛居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市新泰路92號7樓之1",
    "電話": "03-5535850"
  },
  {
    "編號": "43",
    "服務類型": "居家服務",
    "特約鄉鎮": "橫山",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "44",
    "服務類型": "居家服務",
    "特約鄉鎮": "橫山",
    "機構名稱": "信馨居服有限公司附設新竹縣私立信馨居家式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "新竹縣竹北市中泰路6號2樓",
    "電話": "03-5528207"
  },
  {
    "編號": "45",
    "服務類型": "居家服務",
    "特約鄉鎮": "尖石",
    "機構名稱": "社團法人中華民國原住民老人長期照顧暨婦幼受暴緊急安置發展關懷協會附設新竹縣私立拿互依居家長照機構",
    "郵遞區號": "313",
    "地址": "新竹縣尖石鄉嘉樂村2鄰麥樹仁5號",
    "電話": "03-5841968"
  },
  {
    "編號": "46",
    "服務類型": "居家服務",
    "特約鄉鎮": "尖石",
    "機構名稱": "財團法人台灣省天主教會新竹教區附設新竹縣私立長安老人養護中心",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮長安路130號",
    "電話": "03-5943987"
  },
  {
    "編號": "47",
    "服務類型": "居家服務",
    "特約鄉鎮": "尖石",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "48",
    "服務類型": "居家服務",
    "特約鄉鎮": "五峰",
    "機構名稱": "社團法人中華民國五福社會服務協會附設新竹縣私立五福居家式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮幸福路121巷2號9樓",
    "電話": "03-5960775"
  },
  {
    "編號": "49",
    "服務類型": "居家服務",
    "特約鄉鎮": "五峰",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "50",
    "服務類型": "居家服務",
    "特約鄉鎮": "北埔",
    "機構名稱": "社團法人中華民國五福社會服務協會附設新竹縣私立五福居家式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮幸福路121巷2號9樓",
    "電話": "03-5960775"
  },
  {
    "編號": "51",
    "服務類型": "居家服務",
    "特約鄉鎮": "北埔",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "52",
    "服務類型": "居家服務",
    "特約鄉鎮": "峨眉",
    "機構名稱": "社團法人中華民國五福社會服務協會附設新竹縣私立五福居家式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮幸福路121巷2號9樓",
    "電話": "03-5960775"
  },
  {
    "編號": "53",
    "服務類型": "居家服務",
    "特約鄉鎮": "峨眉",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "新竹縣竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "54",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "財團法人台灣省天主教會新竹教區附設新竹縣私立竹北社區式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "竹北市福興東路1段365號3樓",
    "電話": "03-5500797分機311"
  },
  {
    "編號": "55",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "新竹縣私立安歆綜合式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "竹北市四維街228號",
    "電話": "03-5536679分機11"
  },
  {
    "編號": "56",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "財團法人新竹縣天主教世光教養院附設新竹縣私立橫山社區式服務類長期照顧服務機構",
    "郵遞區號": "312",
    "地址": "橫山鄉新興村新興街129號",
    "電話": "03-5932667"
  },
  {
    "編號": "57",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "財團法人新竹縣天主教世光教養院附設新竹縣私立北埔社區式服務類長期照顧服務機構",
    "郵遞區號": "314",
    "地址": "北埔鄉中正路30號",
    "電話": "03-5805586"
  },
  {
    "編號": "58",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "長泰護理之家",
    "郵遞區號": "304",
    "地址": "新豐鄉埔和村埔頂363之10號",
    "電話": "03-5689333"
  },
  {
    "編號": "59",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "社團法人中華民國五福社會服務協會附設新竹縣私立五福社區式服務類長期照顧服務機構",
    "郵遞區號": "303",
    "地址": "湖口鄉中正村達生五路280號",
    "電話": "03-5908205"
  },
  {
    "編號": "60",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "財團法人喜憨兒社會福利基金會新竹分事務所附設新竹縣私立喜歡你社區式服務類長期照顧服務機構",
    "郵遞區號": "307",
    "地址": "芎林鄉文山路565號",
    "電話": "03-5923828"
  },
  {
    "編號": "61",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "新竹縣私立信馨社區式服務類長期照顧服務機構",
    "郵遞區號": "306",
    "地址": "關西鎮中山東路85號",
    "電話": "03-5870550"
  },
  {
    "編號": "62",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "臺北榮民總醫院新竹分院附設社區式長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "竹東鎮中豐路一段81號",
    "電話": "03-5962134分機883、882"
  },
  {
    "編號": "63",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "社團法人中華民國誠馨照顧協會附設新竹縣私立誠馨綜合式服務類長期照顧服務機構",
    "郵遞區號": "310",
    "地址": "竹東鎮興農街119號",
    "電話": "03-5104527"
  },
  {
    "編號": "64",
    "服務類型": "日間照顧服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "新竹縣蒲公英關懷弱勢權益促進協會附設新竹縣私立蒲公英社區式服務類長期照顧服務機構",
    "郵遞區號": "302",
    "地址": "竹北市新泰路92號2樓",
    "電話": "03-5551096"
  },
  {
    "編號": "65",
    "服務類型": "小規模多機能服務",
    "特約鄉鎮": "新竹縣全區",
    "機構名稱": "財團法人新竹縣天主教世光教養院附設新竹縣私立新埔社區式服務類長期照顧服務機構",
    "郵遞區號": "305",
    "地址": "新埔鎮五埔里新關路183號",
    "電話": "03-5882329"
  },
  {
    "編號": "66",
    "服務類型": "家庭托顧",
    "特約鄉鎮": "尖石",
    "機構名稱": "新竹縣私立嘉樂社區式服務類長期照顧服務機構",
    "郵遞區號": "313",
    "地址": "新竹縣尖石鄉嘉樂村麥樹仁5號",
    "電話": "03-5841968"
  }
]




依據資料回應格式準備好DTO(供後續json序列化接收處理)
由於預設api回傳是以中文字定義的Key在程式碼中不可能用中文
這塊可以參照之前JsonProperty部分使用
跟後續序列化需要引入Json.Net套件

 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
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestApp1
{
    class CareInstitution
    {
        [JsonProperty(PropertyName = "編號")]
        public int Id { get; set; }
        [JsonProperty(PropertyName = "服務類型")]
        public string ServiceType { get; set; }
        [JsonProperty(PropertyName = "特約鄉鎮")]
        public string SpecialContractTown { get; set; }
        [JsonProperty(PropertyName = "機構名稱")]
        public string Organization { get; set; }
        [JsonProperty(PropertyName = "郵遞區號")]
        public string PostCode { get; set; }
        [JsonProperty(PropertyName = "地址")]
        public string Address { get; set; }
        [JsonProperty(PropertyName = "電話")]
        public string Phone { get; set; }
    }
}


呼叫api則透過RestSharp套件來實踐

CareInstitutonWebHelper.cs

 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
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestApp1
{
    public class CareInstitutonWebHelper
    {
        public List<CareInstitution> GetCareInstitutionData()
        {
            List<CareInstitution> ApiResult = null;
            var client = new RestClient("https://ws.hsinchu.gov.tw/001/Upload/1/opendata/8774/283/847b3ad3-6b1e-439f-bb7e-869c53727d35.json");
            client.Timeout = -1;
            var request = new RestRequest(Method.GET);
            IRestResponse response = client.Execute(request);
            //Console.WriteLine(response.Content);
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                ApiResult = JsonConvert.DeserializeObject<List<CareInstitution>>(response.Content);                
            }
            return ApiResult;
        }

    }
}





我們想要有一個透過服務類型或路段地址、郵遞區號、特約地區
可以查找到機構資訊的function

 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
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestApp1
{
    public class CareInstitutonWebHelper
    {
        
        public CareInstitution GetCareInstitutionData(string address, string post_code, string service_type, string special_contract)
        {
            List<CareInstitution> ApiResult = null;
            var client = new RestClient("https://ws.hsinchu.gov.tw/001/Upload/1/opendata/8774/283/847b3ad3-6b1e-439f-bb7e-869c53727d35.json");
            client.Timeout = -1;
            var request = new RestRequest(Method.GET);
            IRestResponse response = client.Execute(request);
            //Console.WriteLine(response.Content);
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                ApiResult = JsonConvert.DeserializeObject<List<CareInstitution>>(response.Content);
            }
            return ApiResult.Where(r => post_code == post_code)
                .Where(r => r.ServiceType == service_type)
                .Where(r => r.SpecialContractTown == special_contract)
                .Where(r => r.Address.Contains(address))
                .FirstOrDefault();
        }


    }
}




新增測試專案

 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
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UnitTestApp1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestApp1.Tests
{
    [TestClass()]
    public class CareInstitutonWebHelper_Tests
    {
        [TestMethod()]
        public void GetCareInstitutionData_Test()
        {
            //Arrange
            var CareApi = new CareInstitutonWebHelper();
            var institutions = new List<CareInstitution>();
            var service_type = "居家服務";
            var address = "竹北市四維街228號";
            var post_code = "302";
            var special_contract = "新豐";


            var expected = "新竹縣私立安歆綜合式服務類長期照顧服務機構";

            //Act
            var actual = CareApi.GetCareInstitutionData(address, post_code, service_type, special_contract);

            //Assert
            Assert.AreEqual(expected, actual.Organization);
        }
    }
}



目前直Call api的寫法會導致外部依賴
假如網路斷線或者對方API還沒更新完暫時關閉就無法RUN這塊測試
而這都不屬於我們程式開發可控範疇
因此需要把外部行為隔離出去,想方式去模擬而非真正地去call api。



這裡將Call Api抽離出來額外一個method 重構
並把修飾改為protected virtual

 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
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestApp1
{
    public class CareInstitutonWebHelper
    {

        public CareInstitution GetCareInstitutionData(string address, string post_code, string service_type, string special_contract)
        {
            List<CareInstitution> ApiResult = null;
            ApiResult = CallApi(ApiResult);
            return ApiResult.Where(r => post_code == post_code)
                .Where(r => r.ServiceType == service_type)
                .Where(r => r.SpecialContractTown == special_contract)
                .Where(r => r.Address.Contains(address))
                .FirstOrDefault();
        }

        protected virtual List<CareInstitution> CallApi(List<CareInstitution> ApiResult)
        {
            var client = new RestClient("https://ws.hsinchu.gov.tw/001/Upload/1/opendata/8774/283/847b3ad3-6b1e-439f-bb7e-869c53727d35.json");
            client.Timeout = -1;
            var request = new RestRequest(Method.GET);
            IRestResponse response = client.Execute(request);
            //Console.WriteLine(response.Content);
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                ApiResult = JsonConvert.DeserializeObject<List<CareInstitution>>(response.Content);
            }

            return ApiResult;
        }

    }
}



接著要探討到的就是
如何去模擬call api這個行為

虛設常式 Stub

我們要去模擬外部回傳值或是回應


這邊新增一個目錄Stub好利於分類存放
並新增一個class 命名為CareInstitutonWebHelperStub
將其繼承CareInstitutonWebHelper後再對CallApi進行覆寫
改寫為可以從外部加入假資料的模式

 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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnitTestApp1;

namespace UnitTestApp1_Tests1.Stub
{
    public class CareInstitutonWebHelperStub : CareInstitutonWebHelper
    {

        public List<CareInstitution> FakeApiResult;
        /// <summary>
        /// 模擬api 回傳
        /// </summary>
        /// <param name="ApiResult"></param>
        /// <returns></returns>
        protected override List<CareInstitution> CallApi(List<CareInstitution> ApiResult)
        {
            if (FakeApiResult != null)
            {
                return FakeApiResult;
            }

            return base.CallApi(ApiResult);
        }
    }
}



因此若我們外部有預先傳入FakeApiResult的資料就會將該預先準備的假資料回傳
改寫原先測試專案

 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
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UnitTestApp1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnitTestApp1_Tests1.Stub;
using Newtonsoft.Json;

namespace UnitTestApp1.Tests
{
    [TestClass()]
    public class CareInstitutonWebHelper_Tests
    {
        [TestMethod()]
        public void GetCareInstitutionData_Test()
        {
            //Arrange Real
            //var CareApi = new CareInstitutonWebHelper();
            //Arrange Fake(Stub)
            var CareApiStub = new CareInstitutonWebHelperStub();
            var institutions = new List<CareInstitution>();
            var service_type = "居家服務";
            var address = "竹北市四維街228號";
            var post_code = "302";
            var special_contract = "新豐";

            StringBuilder sbApiResult = new StringBuilder();
            sbApiResult.Append("[");
            sbApiResult.Append("{'編號': '1','服務類型': '居家服務','特約鄉鎮': '竹北','機構名稱': '新竹縣私立安歆綜合式服務類長期照顧服務機構','郵遞區號': '302','地址': '新竹縣竹北市四維街228號','電話': '03-5536679'}");
            sbApiResult.Append(",");
            sbApiResult.Append("{'編號': '5','服務類型': '居家服務','特約鄉鎮': '新豐','機構名稱': '新竹縣私立安歆綜合式服務類長期照顧服務機構','郵遞區號': '302','地址': '新竹縣竹北市四維街228號','電話': '03-5536679'}");
            sbApiResult.Append("]");            
            CareApiStub.FakeApiResult = JsonConvert.DeserializeObject<List<CareInstitution>>(sbApiResult.ToString());

            var expected = "新竹縣私立安歆綜合式服務類長期照顧服務機構";

            //Act Real
            //var actual = CareApi.GetCareInstitutionData(address, post_code, service_type, special_contract);
            //Act Fake(Stub)
            var actual = CareApiStub.GetCareInstitutionData(address, post_code, service_type, special_contract);

            //Assert
            Assert.AreEqual(expected, actual.Organization);
        }
    }
}

如此
即便斷網或者對方API未提供也能做測試驗證了
只不過通常都會需要先得知對方預期的api 回傳結果較好
(備註:雙引號記得要統一取代為單引號)


所以這邊主要重點在於去將有外部依賴的區塊給抽離
並改為具有可更改覆寫彈性



模擬物件 Mock

這邊我們再用一個臺中交通資訊API
http://e-traffic.taichung.gov.tw/DataAPI/swagger/ui/index#!/TraStationAPI/TraStationAPI_Get

有關於台鐵資訊的API


其Swagger API Document十分詳細
這邊我們取一個簡單的
可以列出全部的
http://e-traffic.taichung.gov.tw/DataAPI/api/TraStationAPI
跟指定特定id的
http://e-traffic.taichung.gov.tw/DataAPI/api/TraStationAPI/7310




新增一個TraStationWebHelper.cs  類別封裝api存取的邏輯

 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
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestApp1
{
    public class TraStationWebHelper
    {
        //取得指定之台鐵車站資訊
        public TraStationInfoDTO GetSpecificTraStationInfo(string StationID)
        {            
            return CallApiResult(StationID);
        }

        protected virtual TraStationInfoDTO CallApiResult(string StationID)
        {
            TraStationInfoDTO traStationInfoDTO = null;
            var client = new RestClient($"http://e-traffic.taichung.gov.tw/DataAPI/api/TraStationAPI/{StationID}");
            client.Timeout = -1;
            var request = new RestRequest(Method.GET);
            IRestResponse response = client.Execute(request);
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                traStationInfoDTO = JsonConvert.DeserializeObject<TraStationInfoDTO>(response.Content);
            }
            return traStationInfoDTO;
        }


    }
}
跟相應API回傳JSON要承接反序列化的TraStationInfoDTO.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestApp1
{
    public class TraStationInfoDTO
    {
        public string StationID { get; set; }
        public string StationName { get; set; }
        public string StationAddress { get; set; }
        public string StationPhone { get; set; }
    }
}


TraStationWebHelperStub.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnitTestApp1;

namespace UnitTestApp1_Tests1.Stub
{
    public class TraStationWebHelperStub : TraStationWebHelper
    {
        public TraStationInfoDTO FakeApiResult;

        protected override TraStationInfoDTO CallApiResult(string StationID)
        {
            if(FakeApiResult != null)
            {
                return FakeApiResult;
            }
            return base.CallApiResult(StationID);
        }

    }
}



TraStationWebHelper  對應的測試專案(比照前面STUB作法方式)

 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
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UnitTestApp1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnitTestApp1_Tests1.Stub;

namespace UnitTestApp1.Tests
{
    [TestClass()]
    public class TraStationWebHelper_Tests
    {
        [TestMethod()]
        public void GetSpecificTraStationInfo_Test()
        {
            //Arrange Real
            //var traStationWebHelper = new TraStationWebHelper();
            //Arrange Fake(Stub)
            var traStationWebHelperSub = new TraStationWebHelperStub();
            traStationWebHelperSub.FakeApiResult = new TraStationInfoDTO()
            {
                StationID = "7310",
                StationName = "雙溪",
                StationAddress = "22744新北市雙溪區新基里朝陽街 1 號",                
                StationPhone = "02-24932980"
            };

            var StationID = "7310";
            var expected = "雙溪";


            //Act
            //var actual = traStationWebHelper.GetSpecificTraStationInfo(StationID);
            //Act Fake(Stub)
            var actual = traStationWebHelperSub.GetSpecificTraStationInfo(StationID);

            //Assert
            Assert.AreEqual(expected, actual.StationName);
        }
    }
}

在這案例中傳入的URL其實也算外部依賴也有機會讓測試失敗
在測試階段都會正確但無法得知真正上線運行結果是否也能如預期正常
比方在不設FakeStub且指定的StationID根本不存於列表中
此時可能會跑NULL 錯誤


這邊藉由抽離出抽象層面interface也就是針對call api部分
./WebHelper/IApiHandler.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestApp1.WebHelper
{
    public interface IApiHandler
    {
        string Get(string _url);
    }
}

在自訂一個ApiHandler具體的Class去實作抽象
./WebHelper/ApiHandler.cs

 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
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestApp1.WebHelper
{
    public class ApiHandler : IApiHandler
    {
        public string Get(string _url)
        {
            var client = new RestClient(_url);
            client.Timeout = -1;
            var request = new RestRequest(Method.GET);
            IRestResponse response = client.Execute(request);
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                return response.Content;
            }
            return "";
        }
    }
}

再透過建構子依賴注入到目前helper class當中
使其不依賴具體而是依賴抽象interface
TraStationWebHelper.cs

 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
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnitTestApp1.WebHelper;

namespace UnitTestApp1
{
    public class TraStationWebHelper
    {
        IApiHandler apiHandler;

        public TraStationWebHelper(IApiHandler apiHandler)
        {
            this.apiHandler = apiHandler;
        }

        //取得指定之台鐵車站資訊
        public TraStationInfoDTO GetSpecificTraStationInfo(string StationID)
        {            
            return CallApiResult(StationID);
        }

        protected virtual TraStationInfoDTO CallApiResult(string StationID)
        {
            TraStationInfoDTO traStationInfoDTO = null;
            string input_url = $"http://e-traffic.taichung.gov.tw/DataAPI/api/TraStationAPI/{StationID}";
            var json_result = apiHandler.Get(input_url);
            if (!string.IsNullOrEmpty(json_result))
            {
                traStationInfoDTO = JsonConvert.DeserializeObject<TraStationInfoDTO>(json_result);
            }

            //var client = new RestClient($"http://e-traffic.taichung.gov.tw/DataAPI/api/TraStationAPI/{StationID}");
            //client.Timeout = -1;
            //var request = new RestRequest(Method.GET);
            //IRestResponse response = client.Execute(request);
            //if (response.StatusCode == System.Net.HttpStatusCode.OK)
            //{
            //    traStationInfoDTO = JsonConvert.DeserializeObject<TraStationInfoDTO>(response.Content);
            //}
            return traStationInfoDTO;
        }


    }
}

並把之前和Stub有關的程式都先註解掉不然會開始有錯誤
再來要換成Mock方式來練習

新增自訂的Mock
TraStationWebHelperMock並去實作IApiHandler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnitTestApp1.WebHelper;

namespace UnitTestApp1_Tests1.Mock
{
    public class TraStationWebHelperMock : IApiHandler
    {
        public string Get(string _url)
        {
            if (_url == "http://e-traffic.taichung.gov.tw/DataAPI/api/TraStationAPI/7310")
            {
                var json_result = "{'StationID':'7310','StationName':'雙溪','StationAddress':'22744新北市雙溪區新基里朝陽街 1 號','StationPhone':'02-24932980'}";
                return json_result;
            }
            return "";
        }
    }
}

在Mock中我們去撰寫一個行為利於我們
驗證目標物件與外部相依介面(在此為Url)的互動方式
我們預期
URL傳入近來只要是
http://e-traffic.taichung.gov.tw/DataAPI/api/TraStationAPI/7310
就要回傳此json內容
{'StationID':'7310','StationName':'雙溪','StationAddress':'22744新北市雙溪區新基里朝陽街 1 號','StationPhone':'02-24932980'}


TraStationWebHelper_Tests測試專案程式中

 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
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UnitTestApp1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnitTestApp1_Tests1.Stub;
using UnitTestApp1_Tests1.Mock;

namespace UnitTestApp1.Tests
{
    [TestClass()]
    public class TraStationWebHelper_Tests
    {
        [TestMethod()]
        public void GetSpecificTraStationInfo_Test()
        {
            //Arrange Real
            //var traStationWebHelper = new TraStationWebHelper();
            //Arrange Fake(Stub)
            //var traStationWebHelperSub = new TraStationWebHelperStub();
            //traStationWebHelperSub.FakeApiResult = new TraStationInfoDTO()
            //{
            //    StationID = "7310",
            //    StationName = "雙溪",
            //    StationAddress = "22744新北市雙溪區新基里朝陽街 1 號",                
            //    StationPhone = "02-24932980"
            //};

            //Arrange Fake(Mock)
            var traStationWebHelperMock = new TraStationWebHelperMock();
            var traStationWebHelper = new TraStationWebHelper(traStationWebHelperMock);
            //var StationID = "99";
            var StationID = "7310";
            var expected = "雙溪";

            //Act
            //var actual = traStationWebHelper.GetSpecificTraStationInfo(StationID);
            //Act Fake(Stub)
            //var actual = traStationWebHelperSub.GetSpecificTraStationInfo(StationID);
            //Act Fake(Mock)
            var actual = traStationWebHelper.GetSpecificTraStationInfo(StationID);

            //Assert
            Assert.AreEqual(expected, actual.StationName);
        }
    }
}


此時執行單元測試可成功pass
通常腦筋想法會有點無法轉換過來

畢竟這根本就是自己在設定測試結果阿
當然會每測每過
根本100%會過
這樣子的測試有意義嗎

因為單元測試的重點一直以來
關注的就只有流程跟邏輯而不含外部依賴
若有需要測試到外部依賴就是要去做整合測試
就不算在單元測試範疇





Ref:
Mocks? Stubs? Or Shims?

Mock 入门,分析stub . mock区别

单元测试之Stub和Mock

使用 Microsoft Fakes 隔離測試中的程式碼

使用虛設常式隔離應用程式的各個組件,方便進行單元測試


使用填充碼隔離應用程式以進行單元測試


Microsoft Fakes 入門


Mocks vs Stubs vs Fakes In Unit Testing


Stub vs Mock – How to Make an Intelligent Choice in C#?

【單元測試的藝術】Chap 3: 透過虛設常式解決依賴問題

留言

這個網誌中的熱門文章

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題

經得起原始碼資安弱點掃描的程式設計習慣培養(三)_7.Cross Site Scripting(XSS)_Stored XSS_Reflected XSS All Clients