You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
1부에서도 언급했던, 단단한 기반(초록 막대!)에 한 발을 두면서 새로운 곳으로 발을 옮기는 것과 비슷하다. 한번에 여러 곳을 고치면, 흐름을 따라가기도 어렵고, 최적의 리팩토링을 고민할 시간을 없앤다.
234
238
235
-
... 실제 코드를 짜면서 내가 그걸 할 수 있을지는 모르겠지만.
239
+
... 실제 코드를 짜면서 내가 그걸 할 수 있을지는 모르겠지만.
240
+
241
+
242
+
243
+
---
244
+
245
+
246
+
247
+
# 20장 뒷정리하기
248
+
249
+
#
250
+
251
+
> ~~○ 테스트 메서드 호출하기~~
252
+
>
253
+
> ~~○ 먼저 setUp 호출하기~~
254
+
>
255
+
> **○ 나중에 tearDown 호출하기**
256
+
>
257
+
> ○ 테스트 메서드가 실패하더라도 tearDown 호출하기
258
+
>
259
+
> ○ 여러 개의 테스트 실행하기
260
+
>
261
+
> ○ 수집된 결과를 출력하기
262
+
>
263
+
> **○ WasRun에 로그 문자열 남기기**
264
+
265
+
#
266
+
267
+
`setUp()` 에서 만약 외부자원을 할당받는다면, 테스트가 독립적이기 위해서 해당 자원을 반환해주는 `tearDown()` 이 있어야 할 것이다.
268
+
269
+
간단히 setUp처럼 플래그를 도입하여 할 수도 있지만, 문제가 있다. **플래그는 순서를 표시할 수 없다** 는 것이다. `setUp()` 은 테스트메서드 실행 전에 호출되어야하고, `tearDown()` 은 테스트메서드 호출 후에 실행되어야 한다. 이를 표시하기위해 플래그대신 로그(log)를 도입해보기로 한다.
270
+
271
+
이는 위의 todo-list에서 **'WasRun에 로그 문자열 남기기'**에 해당한다.
272
+
273
+
#
274
+
275
+
```python
276
+
classTestCase():
277
+
278
+
def__init__(self, name):
279
+
self.name = name
280
+
281
+
defsetUp(self):
282
+
'''
283
+
하위 클래스 WasRun에서 오버라이드할 추상 메서드
284
+
'''
285
+
pass
286
+
287
+
deftearDown(self):
288
+
'''
289
+
하위 클래스 WasRun에서 오버라이드할 추상 메서드
290
+
'''
291
+
pass
292
+
293
+
defrun(self):
294
+
'''
295
+
메서드 동적 호출
296
+
getattr(object, name, default)는 object 내에서 주어진 string(name)과 동일한 method를 반환해준다.
297
+
따라서 테스트케이스의 이름을 전달했을 때, 해당 테스트케이스가 호출되었는지를 기록할 수 있다.
여러 테스트를 실행할 때, 테스트 커플링을 피하기 위해(환경을 독립시키기 위해) 사용하는 일종의 세팅 메서드
317
+
'''
318
+
# self.wasSetUp = 1 # 플래그는 순서를 기록할 수 없다. 따라서 테스트 코드 호출전에 setUp과 tearDown의 순서를 보장할 수 없다.
319
+
self.log ="setUp"# 플래그대신 로그를 사용하여 코드의 흐름을 확실히 한다.
320
+
321
+
deftestMethod(self):
322
+
'''
323
+
테스트메서드 호출여부 기록
324
+
메서드가 호출되었는지를 기억(flag)하는 메서드
325
+
'''
326
+
self.log +=" testMethod"
327
+
328
+
deftearDown(self):
329
+
'''
330
+
tearDown 여부 기록
331
+
테스트를 위해 setUp에서 할당받은 외부 자원을, 테스트가 종료되면 반환하는 메서드
332
+
'''
333
+
self.log +=" tearDown"
334
+
335
+
336
+
337
+
classTestCaseTest(TestCase):
338
+
'''
339
+
테스트케이스를 수행하는 메인 클래스
340
+
'''
341
+
342
+
deftestTemplateMethod(self): # testSetUp의 진화! (testRunning 메서드는 이제 필요없음.)
343
+
'''
344
+
setUp과 tearDown의 호출 순서 체크 테스트코드
345
+
'''
346
+
test = WasRun("testMethod") # 이제 test-SetUp과 Running 두 군데서 사용하지 않으니, setUp 메서드도 삭제하고 리팩토링을 되돌린다.
347
+
test.run()
348
+
assert("setUp testMethod tearDown"== test.log) # 플래그 대신에 로그를 검
349
+
350
+
# main
351
+
352
+
TestCaseTest("testTemplateMethod").run()
353
+
```
354
+
355
+
#
356
+
357
+
tearDown이 어떤 외부 자원을 할당받는지는 몰라서 stub으로 구현하긴 했지만, 그건 setUp도 마찬가지니까... 일단 테스트를 통과하는것(초록막대)까지는 확인했다.
358
+
359
+
이 장에서 크게 다룰 것은 없다.
360
+
361
+
1. '플래그'에서 '로그'로 전략을 수정했다는 것, 그 이유는 순서를 남기기 위해서라는 것.
362
+
2. TestCaseTest 클래스에서 테스트 메서드를 `testTemplateMethod()` 로 일원화하였으므로, setUp 메서드의 분리도 삭제하고 리팩토링을 되돌린 점.
363
+
364
+
정도가 정리할 만한 것인듯 하다.
365
+
366
+
367
+
368
+
---
369
+
370
+
371
+
372
+
# 21장 셈하기
373
+
374
+
#
375
+
376
+
> ~~○ 테스트 메서드 호출하기~~
377
+
>
378
+
> ~~○ 먼저 setUp 호출하기~~
379
+
>
380
+
> ~~○ 나중에 tearDown 호출하기~~
381
+
>
382
+
> ○ 테스트 메서드가 실패하더라도 tearDown 호출하기
383
+
>
384
+
> ○ 여러 개의 테스트 실행하기
385
+
>
386
+
> **○ 수집된 결과를 출력하기**
387
+
>
388
+
> ~~○ WasRun에 로그 문자열 남기기~~
389
+
390
+
#
391
+
392
+
지금까지 구현한 코드에서는, 테스트메서드가 실패하면 예외가 발생하여 `tearDown()` 이 제대로 수행되지 않는다. 예외가 발생하건 말건 모든 테스트케이스와 setUp과 tearDown은 다 수행되어야한다.
393
+
394
+
#
395
+
396
+
여러 테스트를 실행하고, 그 결과를 다음과 같이 표현할 수 있다면 좋지 않을까?
397
+
398
+
#
399
+
400
+
> 5개 테스트가 실행됨
401
+
>
402
+
> 2개 실패
403
+
>
404
+
> TestCaseTest.testFooBar-ZeroDivide Exception
405
+
>
406
+
> MoneyTest.testNegation-AssertionError
407
+
408
+
#
409
+
410
+
위와 같이 뜬다면 최소한 어떤 에러들이 테스트케이스에서 발생했고, 무엇을 잡아야 하는지 파악할 수 있게 된다.
411
+
412
+
일단 시작은 사소하게, 테스트 하나의 실행결과를 기록하는 TestResult 객체를 반환하게 해본다.물론 차후에 테스트 여러개도 처리할 수 있게 리팩토링 할 것이다.
413
+
414
+
#
415
+
416
+
```python
417
+
classTestCase():
418
+
419
+
def__init__(self, name):
420
+
self.name = name
421
+
422
+
defsetUp(self):
423
+
'''
424
+
하위 클래스 WasRun에서 오버라이드할 추상 메서드
425
+
'''
426
+
pass
427
+
428
+
deftearDown(self):
429
+
'''
430
+
하위 클래스 WasRun에서 오버라이드할 추상 메서드
431
+
'''
432
+
pass
433
+
434
+
defrun(self):
435
+
'''
436
+
메서드 동적 호출
437
+
'''
438
+
result = TestResult() # 실행결과 객체 할당
439
+
result.testStarted() # 실행 카운트 기록
440
+
self.setUp()
441
+
method =getattr(self, self.name)
442
+
method()
443
+
self.tearDown()
444
+
return result
445
+
446
+
447
+
classWasRun(TestCase):
448
+
'''
449
+
메서드가 실행되었는지를 알려주는 테스트클래스
450
+
'''
451
+
452
+
def__init__(self, name):
453
+
TestCase.__init__(self, name)
454
+
455
+
defsetUp(self):
456
+
'''
457
+
setUp 여부 기록
458
+
여러 테스트를 실행할 때, 테스트 커플링을 피하기 위해(환경을 독립시키기 위해) 사용하는 일종의 세팅 메서드
459
+
'''
460
+
self.log ="setUp"
461
+
462
+
deftestMethod(self):
463
+
'''
464
+
테스트메서드 호출여부 기록
465
+
메서드가 호출되었는지를 기억(flag)하는 메서드
466
+
'''
467
+
self.log +=" testMethod"
468
+
469
+
deftestBrokenMethod(self):
470
+
'''
471
+
실패하는 테스트의 stub
472
+
'''
473
+
raiseException# 이번 장에서는 아직 예외 핸들링을 하지 않았다!
474
+
475
+
deftearDown(self):
476
+
'''
477
+
tearDown 여부 기록
478
+
테스트를 위해 setUp에서 할당받은 외부 자원을, 테스트가 종료되면 반환하는 메드서
479
+
'''
480
+
self.log +=" tearDown"
481
+
482
+
483
+
484
+
classTestCaseTest(TestCase):
485
+
'''
486
+
테스트케이스를 수행하는 메인 클래스
487
+
'''
488
+
489
+
deftestTemplateMethod(self):
490
+
'''
491
+
setUp과 tearDown의 호출 순서 체크 테스트코드
492
+
'''
493
+
test = WasRun("testMethod")
494
+
test.run()
495
+
assert("setUp testMethod tearDown"== test.log)
496
+
497
+
deftestFailedResult(self):
498
+
'''
499
+
실패하는 테스트 수가 제대로 나오는지 체크하는 테스트코드
500
+
'''
501
+
test = WasRun("testBrokenMethod")
502
+
result = test.run()
503
+
assert("1 run, 1 failed"== result.summary())
504
+
505
+
deftestResult(self):
506
+
'''
507
+
실행결과가 제대로 나오는지 체크하는 테스트코드
508
+
'''
509
+
test = WasRun("testMethod")
510
+
result = test.run()
511
+
assert("1 run, 0 failed"== result.summary())
512
+
513
+
classTestResult:
514
+
'''
515
+
테스트메서드(들)의 실행결과를 기록하는 객체
516
+
'''
517
+
518
+
def__init__(self):
519
+
self.runCount =0# 실행된 테스트의 수 0으로 초기화
520
+
521
+
deftestStarted(self):
522
+
self.runCount =self.runCount +1
523
+
524
+
defsummary(self):
525
+
'''
526
+
실행결과 반환 메서드
527
+
'''
528
+
returnf"{self.runCount} run, 0 failed"
529
+
530
+
# main
531
+
532
+
TestCaseTest("testTemplateMethod").run()
533
+
534
+
```
535
+
536
+
#
537
+
538
+
일단 일단 상수들을 변수로 만들어 실제 구현을 몇 하긴 했지만, stub으로 구현한 부분도 많다. 예외가 발생할 경우의 Exception 핸들링이 되어있지 않다. `testBrokenMethod()`에서 실제로 예외가 발생하면 테스트케이스가 모두 실행되지 못하고 종료된다. 따라서 실행 결과도 제대로 얻을 수 없을 것이다.
539
+
540
+
이 문제는 다음 장에서 해결해 보기로 한다.
541
+
542
+
#
543
+
544
+
테스트 하나를 성공시켰는데 그 다음 테스트에서 문제가 생기면, 두 단계 물러서는 것도 고려하라고 한다. TDD는 아주 방어적인 프로그래밍인 것 같다. 절대 두 칸을 건너뛰라고 하지 않는다. 오히려, 다음 칸에 문제가 있어보인다면 일단 뒤로 한칸 가서, 돌아가보라고 조언한다.
0 commit comments