2014 년 5 월죽기전에테스트할것인가, 죽고나서테스트할것인가! Session 1 단위테스트, 그는적인가아군인가? - SimpleSoft 유경상수석컨설턴트
시작하기앞서 단위테스트 (Unit Test) 란? 정의 컴퓨터프로그래밍에서소스코드의특정모듈이의도된대로정확히작동하는지검증하는절차 ( 출처 : 위키백과 ) 주체 개발자 테스터 대상 프로그램소스코드 단위테스트는개발자의적인가아군인가?
피아식별 개발자의적들 다양한기술적, 인적적대관계 빠른기술의변화, xx 한관리자, xx 한고객, 자기자신 프로젝트수행상의적대관계 부실한설계, 짧은개발일정, 잦은요구사항변경, 주적 버그 개발자의우방 개발도구 (Visual Studio!)
좀이른결론 (?) 단위테스트는코드가의도한대로작동하는지검사해준다. 코드가의도한대로작동하지않는것은버그이다. 버그는개발자의주적이다. 따라서 개발자의주적인버그를찾는데도움이되는단위테스트는아군이다!
그러나 어제의아군이오늘의적이될수도있다! 짧은개발환경에서단위테스트를작성할여유가있는가? UI 를갖는코드는테스트코드작성이어렵다. 정말단위테스트는아군인가?
단위테스트의시작 단위테스트는코드조각혹은일정모듈을테스트하는코드 어렵지않아요! 간단한단위테스트 DLL 코드의일부를테스트하는콘솔어플리케이션 서버일부기능을테스트하기위한간단한웹페이지 기타 개발자의대부분은이미단위테스트에대한경험을가지고있음
단위테스트도구 도구가필요한이유 테스트코드가많아짐에따라수작업으로테스트코드관리가어려워짐 테스트결과에대한수집이어려움 개발팀이공유할수있는자동화된테스트필요 단위테스트프레임워크 Visual Studio Unit Test Framework (MSTest) xunit 오픈소스단위테스트프레임워크의일반적인명칭 Nunit, xunit.net, 상용제품들
단위테스트의재시작 가장간단한단위테스트 Smoke Test 유래 하드웨어작성이후전원을넣어칩이타지않는지확인하는테스트 가장간단한단위테스트로서작성한코드를호출해보고예외발생여부만을테스트 결과값에대한확인등의복잡 (?) 한작업은수행하지않음 Tip 가장많이사용되는코드패턴사용 메서드오버로드, 매개변수, 호출방식 수행결과출력 수동으로결과확인가능
초큼발전된단위테스트 테스트대상이의도대로작동하는지테스트 주어진입력 ( 값 ) 에대해의도한작동 ( 결과값 ) 을수행하는지확인 초급단위테스트 미리정의된 ( 계획된 ) 입력에대해의도한결과가나오는지확인 Assertion 활용 Tip 입력값및예상결과값을하드코드하지않을것 당연한결과에대해서도의심할것 Assert 클래스의다양한메서드를활용할것
Go? Stop? 단위테스트는어느정도까지만드는것이좋은가? 무한정단위테스트를작성할수는없다. 가능한모든시나리오를단위테스트화할수없다. 작성한단위테스트로테스트되지않는코드를파악해야한다. Code Coverage Analysis 테스트코드에의해테스트된코드를파악하여시각적으로, 수치적으로표현 테스트되지않은코드들파악 추가적으로작성해야할단위테스트파악
Code Coverage 일정수준단위테스트수행후 Code Coverage 분석수행 테스트되지않은코드파악 추가단위테스트작성및테스트 다시 Code Coverage 분석수행 반복 Tip 개발이어느정도완료된이후 Coverage Test 수행 일정목표수준까지단위테스트및 Code Coverage 반복 단위테스트를처음도입하는경우 30% 정도를목표로단위테스트작성 추후 60 ~ 70% 수준까지단위테스트작성 예외 / 오류상황에대한코드가테스트되지않는경우가많음
오류상황에대한단위테스트 오류상황에서적절한예외가발생하는지테스트 오류를유발하도록상황구성 try ~ catch 를사용하여예상되는예외가발생하는지확인 예외가발생하지않는경우 Assert.Fail 호출 ( 테스트실패 ) ExpectedExceptionAttribute 활용 Tip 정상적으로발생될것으로기대되는예외 ( 들 ) 만을 catch 할것 Exception 클래스를 catch 하는것은테스트의의미가없음 오류유발을위한도구 (Mocking, Faking,..) 활용
고급단위테스트 시나리오구성 다양한조건을구성하여코드가정상적으로작동하는지테스트 각조건별로단위테스트작성 특정조건 ( 시간, 이벤트등 ) 에수행되는코드를테스트하기위해해당조건이유발되도록테스트코드작성 Ex) 파일변경 실제로파일을변경하는테스트코드작성 복잡한검증코드가필요할수도있음 시간, 이벤트등다수의조건들은단위테스트상에서유발시키기어려움 Ex) 날짜변경시어떠한작업을수행해야하는경우
Fakes Framework - 소개 Stub 및 Shim 을사용하여테스트대상코드를다른모듈과격리시킬수있음 미구현모듈이나특정상황에뮬레이션가능 Stub : 특정인터페이스의구현을다르게대체 (mock object) Shim : 컴파일된코드의특정메서드를대체 Visual Studio Premium/Ultimate 에서사용가능
Fakes Framework - 사용법 단위테스트프로젝트에서대상어셈블리선택후 Fake 어셈블리추가 Fake 어셈블리자동생성및참조가추가됨 Stub 클래스및 Shim 클래스는별도의 *.Fakes 네임스페이스에생성됨 IDataRepository db = new Fakes.StubIDataRepository(); db.saveorderint32int32 = (pid, amt) => 99; using (var ctx = ShimsContext.Create()) { ShimBillingManager.AllInstances.CardApprovalStringInt32 = (org, info, amount) => true; var bizlogic = new BizLogic(db); bizlogic.placeorder(1, "CARD", 1000); }
Fakes Framework 고려사항 제약 Stub 인터페이스메서드들 (virtual methods) 에대해서만메서드치환가능 테스트코드에서접근가능한타입들 (public 및 internal) 에대해적용가능 Shim static, non-virtual 멤버및 sealed 멤버들에대해서적용가능 public, internal 및일부 private 메서드들에대해적용가능 메서드본문 (body) 가없는메서드들 (interface, abstract method) 적용불가 http://msdn.microsoft.com/en-us/library/hh549175.aspx 참고 성능 Shim 은런타임에코드가생성되므로느림. Stub 은성능저하거의없음
Fakes Framework - 활용 코드격리 미구현된코드혹은테스트범위가아닌코드를테스트대상코드와격리 Ex) 비즈니스로직테스트를위해데이터조회코드를 Stub/Shim 으로에뮬레이션 항상고정된값을반환하도록 Stub/Shim 으로에뮬레이션 Ex) 날짜에의존적인코드 : DateTime.Now 가항상같은값을반환하도록변경 테스트상황구성 Stub/Shim 을이용하여특정상황에뮬레이션가능 Ex) 오류상황을에뮬레이션하기위해코드의반환값변경
Native Code 단위테스트 VS2012 부터 Native Code 단위테스트지원 Native Code 테스트프로젝트템플릿 C++ 기반단위테스트지원 다양한모듈에대한단위테스트가능 Dynamic Library(.dll) Static Library(.lib) Compiled object file (.obj) Executables (.exe) CppUnitTest.h 단위테스트를위한매크로, 함수정의 다양한 Assertion 함수들 로깅함수들 http://msdn.microsoft.com/en-us/library/hh419385.aspx 참조
단위테스트활용예 단위테스트기반디버깅 1. 버그리포트검토 2. 버그를재현하는단위테스트작성 3. 단위테스트실패확인 4. 버그수정 5. If ( 단위테스트 == Failure) goto 4 6. 버그수정완료
단위테스트주요팁 점진적인단위테스트도입 테스트대상코드격리 (Fakes Framework) Code Coverage 적극활용 단위테스트를위한원본코드변경금지 테스트어셈블리에대해 InternalsVisibleToAttribute 적용 Internal 타입 / 멤버테스트용이 PrivateObject 클래스활용 Reflection 보다쉽게 Private 멤버접근가능 단위테스트수행범위조정 ( 필터활용 ) 단위테스트명명기법활용 Run Tests After Builds 기능활용 ALM 연계 TFS Build System 빌드후단위테스트실패시빌드실패로처리가능
단위테스트기반개발절차 while ( 목표기능 / 모듈개발완료 == false) { 코드작성 ; while(coverage < 목표 Coverage) { 단위테스트 ( 들 ) 작성 ; 작성한단위테스트 ( 들 ) 수행 ; while ( 단위테스트결과 == failure) { 코드디버깅및수정 ; // 단위테스트코드포함 } } } 전체단위테스트수행 ; if ( 전체단위테스트결과 == failure) { 코드디버깅및수정 ; }
단위테스트도입효과 조기에버그발견가능 Smoke Test 만으로도버그발견가능 코드에대한자신감 ( 품질향상 ) 코드변경이미치는영향파악가능 일정수준이상의 Coverage 필요 코드변경에따른다른모듈의영향파악이쉬움 나의코드변경이다른단위테스트의실패를유도한다면잘못된코드변경일가능성이높음 동시개발이용이함 의존성을갖는다른모듈의완성을기다릴필요없이테스트가능
단위테스트의부작용 개발기간부담이늘어남 SI 프로젝트에서적용하기쉽지않음 테스트환경꾸미기가쉽지않은상황이많음 단위테스트는매우자주반복되므로고정된입력에고정된결과가나와야함 데이터베이스기반코드들은시간이흘러감에따라다른조회결과가나올가능성이높음 UI 에대한단위테스트가쉽지않음 Coded-UI Test 로부분적인커버가능 버그를가진단위테스트
제안사항 Solution/Product S/W 개발초기부터단위테스트를작성하고유지 UI 에대한단위테스트포함 Coverage 70% 이상유지 SI Project 서버모듈들에대해서라도단위테스트작성및유지 유지보수에단위테스트가큰도움이될수있음 ( 개발은짧고유지보수는길다!)
결론 단위테스트는아군인가적인가? 단기적으로개발자의부담이증가하지만, 장기적으로개발자의부담을줄여줌. 버그를줄여주며, 코드품질을향상시켜줄수있으므로, 단언컨데, 단위테스트는개발자의든든한우방임. 개발초기부터조금씩단위테스트작성 단위테스트관련도구들적극활용