본문 바로가기

IT Book Summary/TDD

18장-20장 xUnit

18장. xUnit으로 가는 첫걸음

 

테스트 툴을 만드는 과정에서 만들어지는 테스트 툴 자체를 사용하는 것.

- 스스로 자신의 뇌를 수술하는것과 비슷할것이다.

- 자기참조 self-referential 프로그래밍에 대한 전산학 실습

 

우선 테스트 케이스를 만들고 테스트 메서드를 실행할 수 있어야 함.

 

테스트케이스를 작성하기 위해 사용할

프레임워크를 테스트하기 위한

테스트 케이스를 작성해야 한다.

 

할일목록

테스트메서드 호출하기

먼저 setUp 호출하기

나중에 tearDown 호출하기

테스트 메서드가 실패하더라도 tearDown 호출하기

여러개의 테스트 실행하기

수집된 결과를 출력하기

 

첫번째 원시테스트에는 테스트 메서드가 호출되면 true,

그렇지 않으면 false를 반환할 작은 프로그램이 필요하다.

- 테스트 메서드 안에 플래그를 설정하는 테스트 케이스

 

메서드가 실행되었는지 알려주는 테스트케이스 이므로 클래스 이름을 WasRun으로 하자.

플래그 역시 wasRun

 

test = WasRun("testMethod")
print (test.wasRun)
test.testMethod()
print (test.wasRun)

# WasRun 클래스도 생성
# 생성자까지 만들어주자
# wasRun 플래그도 false로 설정
class WasRun:
    def __init__(self, name):
        self.wasRun= None

'None'이 출력되고

실행해보면 testMehod도 정의해 주어야 한다고 한다.

 

testMethod() 안에서 플래그를 설정하자

 

class WasRun:
    def __init__(self,name):
        self.wasRun = None
    def testMethod(self):
        self.wasRun = 1

 

다음은 테스트 메서드를 직접 호출하는 대신 인터페이스 run() 메서드를 사용하는것이다.

 

class WasRun:
    def __init__(self,name):
        self.wasRun = None
    def testMethod(self):
        self.wasRun = 1
    def run(self):
        self.testMethod()

test = WasRun("testMethod")
print (test.wasRun)
test.run()
print (test.wasRun)

 

다음은 testMethod()를 동적으로 호출하는 것.

(테스트 케이스의 이름과 같은 문자열을 갖는 필드가 주어지면

함수로 호출될때 해당 메서드를 호출하게끔 하는 객체를 얻을 수 있음.)

 

class WasRun(TestCase):
    def __init__(self,name):
        self.wasRun = None
        self.name = name
    def run(self):
        method = getattr(self, self.name)
        method()

 

리팩토링의 일반적인 패턴

특별한 사례에 대해 작동하는 토드를 다른 사례에도 작동하게 상수를 변수로 변화시켜 일반화

 

WasRun클래스는 독립된 두 가지 일을 수행한다.

- 메서드가 호출되었는지 그렇지 않은지 기억하는일

- 메서드를 동적으로 호출하는 일

 

TestCase 상위 클래스를 만들어 상속받게 하자

 

name 속성을 상위클래스로 올리자

 

run() 메서드도 상위에 올리자

 

그리고 우리가 작성하는 테스트케이스를 테스트 하는 TestCaseTest 클래스를 만들어 테스트하자

 

# test program in Python

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 = WasRun("testMethod")
#print (test.wasRun)
#test.run()
#print (test.wasRun)

 

첫번째 할일 목록을 끝냈다.

  • 자그마한 단계로 시작
  • 하드코딩 후 상수를 변수로 대체하여 일반성을 끌어내는 방식으로 기능 구현
  • 플러거블 셀렉터를 사용했다. 하지만 이것은 정적코드분석을 어렵게 하므로 지양하자.
  • 테스트 프레임워크를 작은단계로만 부트스트랩 함.

http://tpcg.io/Svse0O0I


19장. 테이블 차리기

 

공통된 패턴

1. 준비 arrange - 객체를 생성한다.

2. 행동 act - 어떤 자극을 준다.

3. 확인 assert - 결과를 검사한다.

 

먼저 setUp 호출하기

처음 단계인 준비단계는 여러 테스트에 걸려 동일한 경우가 있음.

새로운 객체를 얼마나 자주 생성해야하는가에 대한 문제에 직면한다.

이때 다음 두가지 제약이 상충.

  • 성능 - 테스트가 빨리 실행. 객체 하나만 생성해서 모든 테스트가 이 객체를 쓰게 만듬.
  • 격리 - 한 테스트에서의 성공이나 실패가 다른 테스트에 영향을 주지 않게 함.

테스트 커플링을 만들지 말것.

테스트가 돌때마다 객체를 생성. 

WasRun에서 테스트를 실행하기 전에 어떤 플래그를 거짓으로 두기를 원했다.

 

testSetUp 테스트 케이스 작성

 

wasSetUp속성이 없으니 wasSetUp을 설정해주는 setUp메서드를 만들어주고

TestCase에서 setUp을 호출하게 하자.

 

# test program in Python

class TestCase:
    def __init__(self,name):
        self.name = name
    def setUp(self):
        pass
    def run(self):
        self.setUp()
        method = getattr(self, self.name)
        method()
    
class WasRun(TestCase):
    def testMethod(self):
        self.wasRun = 1
    def setUp(self):
        self.wasSetUp = 1

class TestCaseTest(TestCase):
    def testRunning(self):
        test = WasRun("testMethod")
        assert(not test.wasRun)
        test.run()
        assert(test.wasRun)
    def testSetUp(self):
        test = WasRun("testMethod")
        test.run()
        assert(test.wasSetUp)

TestCaseTest("testSetUp").run()

 

한번에 메서드 하나 이상 수정하지 않으면서 테스트가 통과하게 만들 수 있는 방법을 찾아내려고 노력해야함.

 

wasRun 플래그를 setUp에서 설정하도록 하면 WasRun을 단순화 할수 있다.

 

# test program in Python

class TestCase:
    def __init__(self,name):
        self.name = name
    def setUp(self):
        pass
    def run(self):
        self.setUp()
        method = getattr(self, self.name)
        method()
    
class WasRun(TestCase):
    def testMethod(self):
        self.wasRun = 1
    def setUp(self):
        self.wasRun = None
        self.wasSetUp = 1

class TestCaseTest(TestCase):
    def setUp(self):
        self.test = WasRun("testMethod")
    def testRunning(self):
        self.test.run()
        assert(self.test.wasRun)
    def testSetUp(self):
        self.test.run()
        assert(self.test.wasSetUp)

TestCaseTest("testRunning").run()
TestCaseTest("testSetUp").run()

 

  • 일단은 테스트를 작성하는데 있어 간결함이 성능 향상보다 더 중요하다고 생각한다. 일단 객체를 계속 생성.
  • setUp()을 테스트 하고 구현
  • 예제 테스트 케이스를 단순화하기위해 setUp()을 사용
  • 예제 테스트 케이스에 대한 케이스를 단순화하기 위해 setUp() 사용.

http://tpcg.io/ZSzWpi7a


20장. 뒷정리하기

 

할일 목록

테스트 메서드 호출하기

먼저 setUp 호출하기

나중에 tearDown 호출하기

테스트 메서드가 실패하더라도 tearDown 호출하기

테스트 여러개 실행하기

수집한 결과를 출력하기

WasRun에 로그 문자열 남기기

테스트가 계속 독립적이길 원한다면 외부자원을 할당받은 테스트들은 

작업을 마치기전 tearDown 메서드 에서 자원을 다시 반환할 필요가 있음.

 

여러 플래그를 사용하기보다 로그를 남겨 메서드 호출 순서를 알 수있게 하자.

 

플래그 대신 로그를 검사하도록 변경

 

class WasRun(TestCase):
    def testMethod(self):
        self.wasRun = 1
    def setUp(self):
        self.wasRun = None
        self.wasSetUp = 1
        self.log = "setUp "
        
class TestCaseTest(TestCase):
    def testSetUp(self):
        self.test.run()
        assert("setUp " == self.test.log)      

 

wasSepup 플래그 지우기,

테스트 메서드의 실행도 기록

 

여기서 testSetUp 테스트는 실패. 로그가 다르기 때문

 

테스트의 예상되는 값을 바꾸어주자.

 

class WasRun(TestCase):
    def testMethod(self):
        self.wasRun = 1
        self.log = self.log + "testMethod "
    def setUp(self):
        self.wasRun = None
        self.log = "setUp "

class TestCaseTest(TestCase):
    def testSetUp(self):
        self.test.run()
        assert("setUp testMethod " == self.test.log)

TestCaseTest("testSetUp").run()

 

이제 셋업과 테스트 수행한 로그를 잘 반환하는것을 확인.

 

testSetUp 이 두개의 테스트가 할일을 수행하므로 testRunning을 지워주고 ,testTemplateMethod로 이름을 바꾸자.

 

class TestCase:
    def __init__(self,name):
        self.name = name
    def setUp(self):
        pass
    def run(self):
        self.setUp()
        method = getattr(self, self.name)
        method()
    
class WasRun(TestCase):
    def testMethod(self):
        self.wasRun = 1
        self.log = self.log + "testMethod "
    def setUp(self):
        self.wasRun = None
        self.wasSetUp = 1
        self.log = "setUp "
        
class TestCaseTest(TestCase):
    def setUp(self):
        self.test = WasRun("testMethod")
    def testTemplateMethod(self):
        self.test.run()
        assert("setUp testMethod " == self.test.log)

TestCaseTest("testTemplateMethod").run()

 

setUp메서드에서만 쓰이던 분리해 놓았던 WasRun 인스턴스를 되돌려 놓자.

내 사고주기를 설계에 써버리는 것을 좋아하기 때문에, 그냥 리팩토링 - 켄트벡 스타일..

class TestCaseTest(TestCase):
    def testTemplateMethod(self):
        test = WasRun("testMethod")
        test.run()
        assert("setUp testMethod " == test.log)

 

이제 tearDown()을 테스트하자

 

log반환 값을 "setUp testMethod tearDown "으로 테스트

 

TestCase run()에 tearDown 호출 추가.

 

WasRun 클래스에 tearDown 메서드와 로그 더하기 구현

 

테스트 해보면 TestCaseTest에서 에러남.

 

TestCase에 마저 tearDown() 구현

 

테스팅을 마침.

 

# test program in Python

class TestCase:
    def __init__(self,name):
        self.name = name
    def setUp(self):
        pass
    def run(self):
        self.setUp()
        method = getattr(self, self.name)
        method()
        self.tearDown()
    def tearDown(self):
        pass
    
class WasRun(TestCase):
    def testMethod(self):
        self.log = self.log + "testMethod "
    def setUp(self):
        self.log = "setUp "
    def tearDown(self):
        self.log = self.log + "tearDown "

class TestCaseTest(TestCase):
    def testTemplateMethod(self):
        test = WasRun("testMethod")
        test.run()
        assert("setUp testMethod tearDown " == test.log)

TestCaseTest("testTemplateMethod").run()

 

 

할일목록

테스트메서드 호출하기

먼저 setUp 호출하기

나중에 tearDown 호출하기

테스트 메서드가 실패하더라도 tearDown 호출하기

여러개의 테스트 실행하기

수집된 결과를 출력하기

WasRun에 로그 문자열 남기기

 

우리가 이번장에서 한것.

  • 플래그에서 로그로 테스트 전략을 조정
  • 새로운 로그기능을 이용해 tearDown() 테스트하고 구현
  • 문제를 발견했는데 뒤로 돌아가는대신 과감히 수정

http://tpcg.io/R5yMLFLc

'IT Book Summary > TDD' 카테고리의 다른 글

3부 테스트 주도 개발의 패턴. 25장- 28장  (0) 2020.04.07
xUnit 21장 - 24장  (0) 2020.03.31
13-17장  (0) 2020.03.18
10-12장  (0) 2020.03.10
7-10장  (0) 2020.03.09