간단하게 tdd하면서 combinations 구현해보기
tdd : test driven development 테스트 주도 개발
프로그램 설계 후 바로 코드를 작성하는게 아니라 테스트 케이스와 코드를 먼저 작성하고 코드 개발에 들어가는 것이다.
기존 프로세스 : 설계 -> 코드작성 -> 테스트
TDD : 설계 -> 테스트 케이스 및 테스트 코드 작성 -> 코드작성 -> 테스트 -> 리팩토링
매번 TDD를 할 수는 없다. TDD가 비효율적일 때도 있고 효율적일 때도 있다. 언제 TDD를 하는게 좋을까..?
불확실성이 높을 때다. 처음 해보는 주제라서 이게 원하는대로 잘 될지 불확실하다든가, 코드의 성능 측정 등을 통해 계속 바꾸면서 개선시켜야 되는 경우, 고객의 요구조건이 불확실한 경우 등...
자동화된 Test를 통해 피드백이 되기 때문에 코드를 작성하자마자 피드백을 바로 할 수 있고 그 피드백 기반으로 계속 코드를 개선시켜나갈 수 있다. TDD를 함으로써 디버깅 시간이 단축되고 재설계 시간이 단축되는 등 여러가지 이점을 얻을 수 있다. 단점이라고 하면 생산성 저하다. 빠르게 개발 및 통합을 해서 납품하고 다음 프로젝트로 넘어가야되는 SI의 경우에는 TDD를 하기가 현실적으로 좀 힘들다고 할 수 있다.
나는 TDD에 대한 얘기를 보자마자 코딩테스트가 생각났다 ㅋㅋㅋ 그렇지 않나? 우리 코딩테스트 볼 때 정확도를 측정하는 테스트 케이스부터 효율성을 측정하는 테스트 케이스까지 다 통과하는지 보고 나서야 문제를 맞췄다고 이야기한다. 한 개의 테스트 케이스라도 통과하지 않으면 코드를 수정하거나 처음부터 로직을 재설계한다. TDD도 그런거 아닐까?
from itertools import combinations
def mycombi(lst,num):
def dfs(lst,num,result,tmp):
if num == 0:
result.append(tuple(map(lambda x:lst[x],tmp)))
return
if not tmp:
for i in range(len(lst)):
tmp.append(i)
dfs(lst,num-1,result,tmp)
tmp.pop()
else:
for i in range(tmp[-1]+1,len(lst)):
tmp.append(i)
dfs(lst,num-1,result,tmp)
tmp.pop()
result = []
dfs(lst,num,result,[])
return tuple(result)
N = 10
lst = [i for i in range(5)] + [i for i in range(5)]
for i in range(3,N):
correct_answer = set(combinations(lst,i))
my_answer = set(mycombi(lst,i))
if correct_answer == my_answer:
print("PASS")
else:
print("FAIL")
굵은글씨 친 부분이 테스트케이스를 생성하고 테스트하는 부분이다. 물론 이 코드는 TDD라고 하기엔 너무 허접하다 ㅋㅋㅋ 근데 뭐 대충 큰 흐름은 비슷하지 않을까? 난 저 테스트코드를 먼저 작성하고 위에서 내 로직을 작성했다. 그리고 테스트 결과 실패가 나왔었고
코드를 다시 수정해서 PASS를 만들어냈다.
마치 코딩테스트하듯이 말이다.
실제로 TDD개발을 할 때에는 더 신경써야 될 게 있다.
1. RED GREEN BLUE 단계가 뭔지 알고 들어가자.
RED는 실패하는 테스트코드 작성단계
GREEN은 테스트코드를 통과하기 위한 로직 작성 단계
BLUE는 중복 코드 제거, 일반화 등 리팩토링 단계
RED -> GREEN -> BLUE -> RED -> GREEN .. 이렇게 반복해나간다.
2. 테스트케이스를 신경써서 만들어야된다.
경계값 테스트, 큰 값이나 대량의 값 테스트(성능측정) 등등..
jUnit이라는 툴이 많이 쓰인다고 한다. 나중에 공부해봐야지.
테스트케이스가 통과하는 선에서 코드 중복을 줄이도록 combination 코드를 좀더 개선시켜봤다.
def mycombi(lst,num):
def dfs(lst, num,last_i, result, tmp):
if num == 0:
result.append(tuple(tmp[:]))
return
for i in range(last_i+1,len(lst)):
tmp.append(lst[i])
dfs(lst, num-1, i, result, tmp)
tmp.pop()
result = []
dfs(lst, num, -1, result, [])
return tuple(result)