이번학기 SW 개발방법 및 도구에서 TDD(test driven development)에 대해 공부했다.
이는 test driven 즉, 통과하고자 하는 test를 지정한 후 “test를 통과하기 위해서” 개발을 진행하는 것이다.
test driven development
- Add a test
-
빈 code (declare 부분이나, 이름 및 입출력 parameter만 지정된 코드)를 바탕으로, 구현하고자 하는 내용이 담긴 test code 작성
-
예상 결과 및 오류동작 등등 모든 senario를 고려해야 함.
- Run all tests and see if the new test fails
-
두번째 cycle 이후부터는 모든 코드를 다 돌려봐야 한다. 그래야 새로 만든 코드가 기존의 기능을 해치지 않았는지 확인 가능
-
만약 3 전에 test를 통과했다면, test code가 잘못된 것
- Write the codes
- 필요한 기능만 구현하기 (욕심 x)
- Run tests
- 3에서 만든 production code가 2에서 실패한 test code를 통과하는지 확인
- Refactor code
-
기본 동작에 문제 없는 코드를 “수정”한다.
-
중복을 제거하여 가독성을 높히고, 부가적인 기능을 구현한다.
이를 수행하기 위한 test code들을 간단히 체험해보기 위해, 아래와 같은 실습을 진행하고자 한다.
단어를 입력하면 각 글자의 ascii code의 합을 반환하는 함수를 만들자.
def total_ascii(word):
"""
input: word
output: sum of ascii of letters in word
"""
string = "".join(word)
result = 0
for letter in string:
result += ord(letter)
return result
만약 midan
을 입력하면, m(109)+i(105)+d(100)+a(97)+n(110) = 521 가 나와야 한다
이를 확인하기 위해 assert
와 unittest
를 이용하자.
# using assert
midan = total_ascii('midan')
assert midan == 521, "wrong answer!"
print("test is completed!")
print(midan)
assert midan == 520, "wrong answer!"
print("test is completed!")
print(midan)
첫번째 경우에는 “test is completed! “ 그리고 521이 출력되며,
두번째 경우에는 AssertionError: wrong answer! 가 출력됨을 확인할 수 있다.
이렇게 하나의 입출력을 확인하는 test case를 실행해봤다.
이제 여러 test case가 모인 test suite를 해보자.
이때 이용할 unittest는 python에 내장되어 있는 class 이다.
이번엔 mockup을 이용해보자.
cclass ascii_string(object):
def __init__(self):
self.word = ""
self.num = 0
def set_data(self, data):
self.word += data
def set_number(self, num):
self.num = num
def total_ascii(self):
"""
input: word
output: sum of ascii of letters in word
"""
string = "".join(self.word)
result = 0
for letter in string:
result += ord(letter)
return result
def num_to_ascii(self):
"""
input: int between 0 ~ 127
output: letter matched with input number
"""
if self.num > 127:
raise Exception("Too large number")
return chr(self.num)
위와 같은 class를 임의로 정의하고 실행하기 위해, unittest를 이용해보자.
import unittest
class test_ascii_string(unittest.TestCase):
def test_total_ascii(self, data):
a = ascii_string()
a.set_data(data) #midan
a.ascii_string()
self.assertionEqual(521) #right
#self.assertionEqual(520) #fail
def test_num_to_ascii(self):
b = ascii_string()
b.set_data(110)
b.num_to_ascii()
self.assertionEqual('n')
if __name__ == '__main__':
unittest.main()
from unittest import TestCase
from unittest.mock import patch
class Test_ascii_string(TestCase):
@patch("requests.post")
def test_total_ascii(self, mock_post):
response = mock_post.return_value
response.status_code = 201
response.json.return_value = {"id": 99}
a = ascii_string()
a.set_data('midan') #midan
a.total_ascii()
self.assertionEqual(521) #right
mock_post.assert_called_once_with(
"https://midannii.github.io/",
data={"wrong task"},
)