통화 예시는 이제 끝났다. 이제는 xUnit에 대한 예시를 위해 python에서의 TDD의 좀 더 교묘한 활용을 보게된다.
chapter 18
크기를 넓혀서 테스트 뿐 아니라 테스트 프레임워크를 만들어 테스트를 해보도록 한다.
테스트 프레임워크를 만드는데도, 가장 먼저 할 일은 테스트를 작성하는 것이다.
파이썬을 사용한다고해도, 이전에 배웠던 TDD의 기본 로직은 변하지 않는다. 똑같이 테스트와 구현, 중복 제거를 진행한다.
getattr
파이썬에선 메서드의 이름을 함수처럼 다룰 수 있는데, 이를 활용해 테스트 코드를 보다 간단히 할 수 있다.
class wasRun:
def __init(self, name):
self.wasRun = None
self.name = name
def run(self):
method = getattr(self, self.name)
method()
def testMethod(self):
self.wasRun = 1
test = wasRun("testMethod")
test.run()
위와 같은 코드에서, testMethod
를 name으로 넣고, run()
에서 getattr()
를 통해 받아서 실행하게 할 수 있다.
이 후 run()
코드를 상위 클래스에 두어 모든 테스트에서 run()
을 통해 테스트를 진행할 수 있도록 구현할 수 있다.
TestCaseTest
class TestCase:
def __init__(self, name):
self.name = name
def run(self):
method = getattr(self, self.name)
method()
class WasRun(TestCase):
def __init__(self, name):
self.wasRun = None
TestCase.__init__(self, name)
def testMethod(self):
self.wasRun = 1
class TestCaseTest(TestCase):
def testRunning(self):
test = wasRun("testMethod")
assert(not test.wasRun)
test.run()
assert(test.wasRun)
TestCaseTest("testRunning").run()
처음 완성한 테스트는 위와 같다. test가 run했는지를 확인하기 위한 테스트인데, test F/W을 만드는데 test를 하고 있는 미묘한 상황이다.
chapter 19
3A (pattern)
테스트를 작성하다보면 발견하게되는 공통된 패턴.
- arrange(준비) - 객체를 생성한다.
- act(행동) - 어떤 자극을 준다.
- assert(확인) - 결과를 검사한다.
성능과 격리
테스트를 작성하다보면 3A 패턴을 확인하게되고, 여러 테스트에서 1번 arrange가 동일하게 사용되는 경우를 확인할 수 있다.
예를 들면, 7과 9의 사칙연산에서 각 사칙연산 테스트는 4개지만, 7과 9는 모든 테스트에서 사용된다. 이러한 객체들을 새로 생성하는가에 대해 두 가지 제약이 상충한다.
- 성능 - 테스트가 될 수 있는한 빨리 실행되길 원함. 여러 테스트에서 같은 객체를 사용한다면, 하나의 객체 생성으로 모든 테스트에서 사용하게 함.
- 격리 - 하나의 테스트의 성공과 실패가 다른 테스트에 영향을 주지 않길 원함.
이런 제약에서 당연히 테스트 커플링을 만들지 않는 방향으로 가는 것이 좋다.
테스트 커플링이 발생하면, A 테스트 후 B 테스트시 성공하더라도 B 테스트 후 A 테스트 시 실패하는 등 예기치 못한 동작들이 발생하기 쉽다.
chapter 20
중복이 여러 개 생길 때까지 기다리는 것이 아니라 바로바로 중복이 나올 때마다 리팩토링을 한다. (켄트 백 방식)
chapter 21
테스트 구현 순서
- 테스트를 통해 얻을 수 있는 것이 있고, 테스트를 만들 수 잇다는 확신이 드는 것부터 만든다.
- 테스트를 하나 성공시켰는데, 그 다음 테스트를 만들면서 문제가 생기면 다시 돌아가서 생각해보도록 한다.
- 모든 테스트가 성공하던 시점을 체크포인트로 삼는 것.
필요한 것을 빠르게
TDD의 기본은 필요한 것들을 빠르게 만드는 것이다. 진짜가 될 가짜 구현을 만드는 것.
- 어떤 것이 필요한지를 확인한다.
- 예를들면 Test에
testBrokenMethod()
를 예상하여 만든다.
- 예를들면 Test에
- 필요한 것을 빠르게 만든다.
- test broken 상태는 많은 경우와 처리방식이 있겠지만,
raise Exception
같이 핸들링하지 않고 우선 빠르게 생성한다. 아직 예외를 잡고 고치지 않았지만, 분명 선언을 한다는 것이 중요하다.
- test broken 상태는 많은 경우와 처리방식이 있겠지만,
chapter 23
collecting parameter
매개변수 수집이라고 번역된 이 패턴은 메서드가 매개변수를 받아서 처리하는 것.
예를 들면, 아래와 같은 코드처럼 적용이 된다. (별 것도 아닌데 거창하게 적어놨다..)
- 장점은
run()
이 명시적으로 return하지 않아도 된다는 점이다. - 하지만 저 목적보단 메서드가 명확한지, 논리적인지가 중요하다. 여기선
run()
이 할당하는 부분과, 할당 후 테스트를 수행하는 부분으로 나뉘는데 이는 좋지 않으므로 할당을 위에서 해주는 방식으로 수정한다.
```python #1 def run(self): result = TestResult() … #use
#2 def run(self, result): … #use ```
chapter 24
xUnit
xUnit은 30개 이상의 프로그래밍 언어에 포팅되어 있고, 아마 사용하려는 언어에 이미 되어있을 것.
앞선 실습처럼 xUnit을 직접 구현함으로써 얻을 수 있는 것이 있다.
- 숙달: 직접 만들어보면, xUnit을 이해하기 쉬움.
- 탐험: 새로운 언어를 접할 때, 간단한 xUnit을 만듦으로서 기능들을 경험.
- 마틴 파울러: 소프트웨어 공학 역사에 이토록 많은 사람이 이렇게 짧은 코드로 이토록 큰 은혜를 입은 적이 없었다.
TDD python
하나 하나 따라가지 않았지만, python에서 TDD를 사용하는 방법은 java에서와 유사했다.
동일하게 TDD의 원칙에 따라 테스트와 함께 코드를 작성해나갔고,
다른점은 python의 언어 특징에 맞게 리팩토링 및 수정이 진행되었다는 점이다.