아래 내용은 오픈마루 루비 스터디(7/31)에서 공유하기 위해 작성한 내용입니다.
단위 테스트(Unit Test)?
단위테스트에 대한 오해
- 단위 테스트는 회귀 테스트(regression test)나 수용성 테스트(acceptance test)가 아니다(그 역할을 약간 도울 뿐).
- 단위 테스트는 시스템 유닛을 잘 디자인하기 위한 도구다.
현재 개발 중인 유닛을 잘 만드는데 집중하기 위해, 다른 건 일단 잊자.
Test Double은 내 머릿속의 지우개!
Test Double
SUT(System Under Testing)가 의존하는 구현체의 일부를 "테스트에서만 사용하는 것"으로 바꾼다.

그림 출처: http://xunitpatterns.com/Test%20Double.html
-
Dummy
-
Fake
- 매번 myID에 호출하지말고, 개발환경에 가짜 myID 하나 두지?
-
Stubs
- 거, 테스트할때마다 메일 좀 안 보내면 안돼? 메일 보내는 부분만 바꿔치기하면 되잖아
-
Mocks
- 너는 어떤 메시지를 주고 받는지 잘 감시해서 나한테 알려줘야해
Verification
테스트는 검증(verification)의 과정이다. 검증은 2가지 방법으로 할 수 있다.
-
상태(state) 검증
- 300원 인출했어. 너 이제 500원 있는거 맞지?
-
행위(behavior) 검증
- 나는 분명히 너한테 300원 인출하라고 시켰다. 알아서 잘 처리할거라 믿어!
Mock과 Stub의 차이는 어떤 검증 방법의 차이.
TDD 고전주의자
왠만하면 실제 객체를 그냥 사용하자. 불가피한 경우(ex. 네트워크 통신)는 스텁을 만든다.
아래는 acts_as_cached_test.rb. ActsAsCached의 일부를 스텁으로 대체한다.
- class CacheCache < Hash
def setted?(name)
not self[:set][name].nil?
end
end
$cache_cache = CacheCache.new
module ActsAsCached
module Disabled
def set_cache_with_disabled(*args)
key = cache_key(args.first)#.tap{|x| p x}
$cache_cache[:set][key] = args[1]
args[1]
end
end
end
그리고 상태를 검증한다.
- it "should cache tag_list" do
@note.tag_list(@user)
cache_store.should be_setted("Note:4:tag_list")
end
TDD 뫅주의자
뭔가 특별한 행위를 하는 객체가 있다면 항상 mock을 사용한다.
- it "should refresh recent pages cache" do
flexmock(Page).should_receive(:set_cache).once.
with('/recent_pages/7/ko', any, any).and_return []
get :recent_pages, :id => 7, :refresh => true, :lang => 'ko'
end
여기서 좀 더 발전하면, 객체들의 행위들을 잘 관찰하고, 검증하면 개발을 잘 할 수 있다고 믿는 BDD(행위 주도 개발)에 다다른다. BDD는 TDD의 용어를 행위 중심으로 개정한 뫅주의자들의 작품이다.
고전주의자 vs. 뫅주의자
양쪽 모두 자신들의 논지가 있다. 하지만 뫅주의자들이 설득의 필요를 더 느끼므로 이 쪽 논리가 더 발전되어 있다. 고씨와 뫅씨의 대화를 들어보자.
- 뫅: mock 쵝오!~ mock만이 단위테스트를 진정으로 고립시키는 유일한 방법이야. 어이 고씨, 당신이 하고 있는건 단위테스트가 아니야. 그건 미니 통합 테스트라규~
- 고: 그게 현실이야 이 친구야. 당신 왕따 아니야? 다른 모듈이 수정되도 당신은 모르고 있을것 같아. 그럼 그게 더 큰 문제일껄?
- 왁: 모듈 하나가 깨지면, 거기에 연관된 모든 테스트가 연쇄적으로 깨져버릴꺼여. 그러면 어떤 모듈이 문제가 되는지 빨리 알아챌 수 있겠어? 아마 이 테스트, 저 테스트 돌아다니면서 한참을 헤매야 할껄?
- 고: 모르는 소리! 난 내 시스템을 잘 알고 있다구~ 그깟 에러 금방 고쳐주지! 뫅씨 테스트 코드는 너무 장황해서 읽기가 힘들어. 뭐 그렇게 서론이 길어? 게다가 테스트만 봐도 실제 구현 코드가 보일 정도라. 그건 중복이고 불필요한 노력이라구. 리팩토링도 힘들겠고 말이지
- 뫅: 테스트만 봐도 구현이 보이는건 장점이야 이 친구야. 테스트가 진정한 문서로 거듭나고 있다는 의미지! 이렇게 실용적인 스펙을 본 적이 있나? 그리고 요즘 세상이 좋아져서 그깟 커플링은 느슨하게 만들수도 있다구.
- 고: 도대체 그렇게 고생해가면서 mock을 지킬려는 진짜 의도가 머야? 뇌물 먹었어?
- 뫅: 무식한 사람 같으니. mock은 디자인에 도움이 된다구! 일명 있다치고 접근법이지. 잘 들어봐. 처음 개발을 시작할 때 참 막막하잖아? 그니까 먼저 사용자눈에 보이는 UI 부분부터 만드는거야. 도메인 모델은 있다 치는거지. UI를 다 만들면 그 다음에 있다쳤던 모델을 스펙대로 만들기만 하면 된다는 말이야. 이게 얼마나 큰 도움이 되는 줄 알아? 이게 바로 outside-in 접근법이란 말이지. 당신 보니까 모델이랑 UI 오가느라 정신 없던데 그래서 오래 살겠나?
- 고: 훗, 요즘은 레이어 별로 개발하는데 익숙치 않은 사람이 어딨나? MVC는 기본이라구. 이미 몸에 익어서 하나도 어색하지 않아.
- 뫅: 당신 코드를 보니 불필요한 질의 메서드가 많던걸? 그거 테스트에서만 쓸꺼 아닌가? 뭐하러 만드나? Tell Don't Ask 원칙도 모르나?
- 고: 이게 다 완벽한 도메인 모델을 만드는데 도움이 되는거라구. 그리고 이런건 사소한 문제일 뿐이야.
이 논쟁을 마치려면 손석희씨가 필요할 것 같다. 좀 더 잘 정리된 문서를 원하면 Mocks Aren't Stubs을 읽어보도록.