코딩 테스트 및 알고리즘/혼자서 이것저것 풀어보기

간단하게 tdd하면서 combinations 구현해보기

띠리링구 2022. 3. 28. 18:46

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)