지난주 이런 저런 사고가 발생해서 정신없이 보내긴 했지만, 그래도 틈틈이 머리도 식힐 겸 JUnit 코드 분석을 시도했다.

3.8은 Cook’s Tour라는 JUnit 핵심설계의 과정을 담은 문서도 있고, Python이긴 하지만 TDDBE에 간단히 만드는 과정도 나와있고 해서 사실 분석하기가 어렵지 않았다. 3.8을 바로 보자면 조금 복잡한 느낌도 있지만, 초창기 버전인 1.0 소스부터 공개되어 있고, 2.0에서 살짝 확장되긴 했지만 거의 초기 설계와 코드를 그대로 유지해오고 있기 때문에 단계적으로 발전하는 과정을 보면서 분석하기에는 아무런 문제가 없었다.

오브젝트 프레임워크 구조인 1.0~3.x와는 달리 애노테이션을 사용하는 DSL스타일의 프레임워크로 바뀐 4.x의 코드는 처음 딱 열어볼 때부터 좀 복잡하다는 인상이 들었다. 단순한 setup-test-teardown구조가 아닌, 각종 애노테이션의 부가적인 기능의 조합(expected, timeout, ignore, beforeclass…, filtering, sorting)을 모두 처리해야 하고, 오브젝트 프레임워크가 아니니 8가지 패턴으로 깔끔하게 만들어진 3.x와는 다를 거라고 생각하기는 했지만, 좀 처럼 쉽게 눈에 들어오는 구조와 코드들이 아닌 것이 내심 불안했다.

켄트 벡이 소개한 JUnit 4.x에 적용했던 두가지 구현(혹은 설계) 패턴(nested method object, conditional factory)을 알고 있지만 그 것만으로는 프레임워크의 기본 구조를 이해하는 것도 쉽지 않았다. 코드를 따라 그림을 그려가면서 한참을 들여다 보고는 겨우 구조와 동작원리가 이해가 됐다. 3.x의 틀과는 완전히 딴판이다. 단지 애노테이션 방식으로 바뀌었기 때문은 아니다. 훨씬 더 근본적인 프레임워크의 확장과 유연성을 고려한 설계의 노력이 느껴지는 코드이다.

하지만 잠시 들여다본 코드는 그다지 인상적이지 않았다. 오히려 조금 실망스러웠다. 4.0이후로 메이저 업그레이드가 있었던 것도 아닌데.벌써 deprecated된 코드들이 제법 있다. 그러고 보니 켁트 벡이 JUnit의 공동개발자인 David Saff에게 "엉망인 코드를 정리하는데 함께 애써줘서 고맙다"는 얘기를 한 것이 기억난다. 그만큼 JUnit 4는 처음부터 실험적이고 모험적인 시도였던 것 같다. 한편으로는 오브젝트 프레임워크가 아니고, 그에 따라 테스트 클래스와 프레임워크의 결합이 거의 없는 수준이니 한편으로는 조금 자유롭게 다양한 실험을 해볼 수 있었던 것일지도 모르겠다.

4.7에서 도입되어서 열심히 실험중인 Inteceptor는 기존의 nested method object와 default runner builder 같은 개념을 좀 더 유연하게 확장하게 만들어준다. 예를 들면 @Test(expected=)를 이용해서 예외를 확인하는 기능을 이제는 인터셉터를 써서 대신할 수 있다. 아마도 테스트 메소드를 지정하는 것을 제외하면 거의 모든 기능을 인터셉터를 써서 새롭게 적용할 수 있을 것이다. 애노테이션이라는 이미 고정된 DSL 표현식을 넘어서서 다시금 오브젝트 프레임워크 시절의 무한한 확장가능성을 JUnit에 넣어줄 수 있는 것이 Inteceptor가 아닐까 싶다. 그래서 처음 이 Interceptor를 JUnit에 도입하던 날 켁트 벡이 흥분해서 트위터에 "JUnit geek들은 어서 최신 소스를 받아서 이 Interceptor를 써보라"고 했던 것 같다. 아마도 이 Interceptor가 중심이 되어서 JUnit이 새롭게 구성되는 시점이 5.0이 나오는 때가 아닐까 하는 예측을 해본다.

 

아무튼 JUnit 4.7는 아직 좀 지저분하다. 다양한 시도와 확장을 고려한 흔적들이 보인다. 어쩌면 필요없을 것 같은, 과도하게 유연하게 만든 코드들도 보인다. 언젠가 deprecated될지 모르겠다. 특히 internal 파트는 그렇다. 아직 4.x용 Cook’s tour가 나오지 않은 이유가 그런 것일지도 모르겠다. 어쨌든 좀 더 분석을 해보면 재밌는 모습을 찾을 수 있을 것 같다. 다양한 실험적인 시도들(theory, assume, maxcore, parallel, interceptor)도 살펴보는 것도 재밌을 것 같고.

 

한가지 떠오른 생각이 있다면. JUnit 4.x의 테스트 코드는 애노테이션을 제외하면 사실 프레임워크 구현에 매여있지 않다. 그렇다면 그 애노테이션을 그대로 사용하면서 새로운 테스트 프레임워크를 만들어보면 어떨까 하는 생각도 들었다. JUnit을 대치할 이유는 없으니, 뭔가 실험적이고 학습에 도움이 되는 시도를 해보는 것도 재밌을 것 같다는 생각이 들었다. JUnit의 nested object method 패턴을 스프링의 AOP나 callback/template를 이용해서 만들어보면 어떨까 하는 생각도 든다. 스프링으로 만드는 JUnit4 호환 테스팅 프레임워크라? 이거 구미가 땡기는데… 4.7 코드 분석이 모두 끝나면 한번 시도해 볼까?

최근에 1.2.1로 업데이트된  JUnitMax의 가장 큰 변화는 바로 자동으로 실행되는 테스트를 선택할 수 있는 기능이 추가된 것이다. JUnitMax는 기본적으로 프로젝트의 모든 테스트를 실행한다. 회귀테스트의 개념에 따라 지금 수정한 내용이 다른 테스트에 영향을 주는지 확인하는 것은 중요하고, 그래서 자주 모든 테스트를 수행하는 것이 필요하다.

하지만 경우에 따라 수행시간이 매우 길고, 시스템에 부하를 주는 통합테스트나 웹 테스트, 스프링 컨테이너 테스트, DB테스트 등이 포함될 수 있고, 그때문에 전체 테스트를 매번 코드를 수정할 때마다 실행하는 것이 비효율적일 수 있다.

그래서 최신 JUnitMax에서는 원하는 패키지만 선택해서 자동 테스트를 실행하거나, 특정 패키지의 테스트만 자동 테스트에서 제외시킬 수 있다. 전체 테스트 수행시간이 수십분~몇시간이 걸리는 크고 복잡한 프로젝트에도 이제 JUnitMax를 적용 시킬 수 있다.

JUnixMax의 블로그는 : http://www.threeriversinstitute.org/blog/

개발자인 켄트 벡의 Twitter는 http://twitter.com/KentBeck 이다. JUnixMax를 개발하는 중에 느낀점, 이클립스의 플러그인을 개발하는 것에 관한 불평, 개발하면서 얻은 교훈 등에 관한 재밌는 이야기가 실시간으로 올라온다.

WebDriver라는 웹 테스팅 툴을 살펴보고 있다. 기존의 자바스크립트를 이용한 웹 테스팅 툴의 단점을 극복하기 위해서 직접 브라우저를 컨트롤 해서 더 네이티브한 방법으로 테스트를 할 수 있게 한다고 한다. 개발자 입장에서 웹 테스트에 대해서 접근하는 패턴을 적용한 툴이라는 점이 흥미롭다. 구글의 테스팅팀의 블로그에서 추천을 해준 것이기도 하다. 특히 PageObject 패턴이라는 것에 관심이 간다. 웹 UI에서의 사용자의 행위를 모델링 해서 그것을 이용해서 테스트를 작성할 수 있도록 해주는 것이다. 단순한 웹 액션 레코드+플레이 방식의 스크립트를 이용한 테스트보다 좀 더 진보된 방법이 아닐까 싶은데, 아직 안써봐서 잘 모르겠다.

참, Selenium 1.5에 포함이 될 거라고 한다.

James Carr가 TDD Anti-Patterns을 공개했다. TDD라고 되어있지만 사실은 테스트(코드작성) 안티패턴이라고 하는 것이 맞을 것이다. TDD라는 용어가 많은 경우 개발 중에 테스트코드를 작성하는 것이라는 의미로 사용되고 있는데.. Test During Development라고 생각하면 될 듯.

아무튼, 테스트를 작성할 때 종종 발생하는 실수를 패턴으로 정리한 내용이다. 여기서 다시 top 10을 뽑아서 상세한 설명과 증상, 예제, 해결방법 등에 관해서 연재하겠다고 하니 기대해봐야겠다.

TDD는 둘째치고 그냥 테스트코드 작성을 제대로 하기도 사실 쉽지 않다. 대부분의 테스트 관련 책에서는 테스트를 구체적으로 어떻게 만들어야 하는지에 대해서 그다지 알려주는 내용이 없다. 결국은 많은 주먹구구식의 테스트 작성으로 시행착오를 겪으며 끈기를 가지고 경험을 통해서 배우는 수밖에 없다.  적지 않은 개발자들이 그런 이유로 중간에서 테스트 작성을 포기하기도 한다.

그래서 테스트에 처음 도전하는 개발자들은 마치 자바문법을 겨우 익히고 OOP개념서를 한번 읽고나서 실전에서 처음 자바애플리케이션을 개발해야 하는 것과 같은 막막함을 느낀다. 테스트작성과 관련해서 Effective Java나 J2EE Design and Developmnent와 같은 책이 좀 나와줬으면 좋겠다. 특히, 대부분의 자바개발자들에게 필요한 것은 데이타베이스를 사용하는 웹 애플리케이션을 작성할 때의 테스트 방법이다. 효과적인 웹 인터페이스를 가지는 CRUD 테스트의 작성 방법은 어떤 것인가쯤은 다뤄줘야 하지 않을까.

스프링 3.0은 18개의 모듈과 100개 가까운 직접 의존 라이브러리로 구성되어있다. 스프링으로 새로운 프로젝트를 만들때 이 모듈과 라이브러리 중에서 어떤 것을 선택해야 할지 결정하는 것이 매우 부담스러운 일이다. 이를 위해서 이전에 각 모듈의 의존 라이브러리에 대한 분석을 한 적이 있는데, 그 뒤로 계속 라이브러리가 추가,삭제 되거나 버전이 바뀌고, 심지어 스프링 모듈자체에도 변경이 있어서 기존 정보와 최신 버전의 스프링3의 의존관계 정보는 조금 차이가 난다.

스프링 빌드에 의해서 생성되는 POM이나 Ivy 설정정보에서 각 모듈의 의존 라이브러리의 정보와 버전, 스코프, 옵션 여부 등에 대해서 알 수가 있는데, 이를 엑셀로 해서 매트릭스를 만들면 보기가 편하다. 일전에 작업한 것을 구글 스프레드시트에 공개했었는데, 계속 수작업으로 업데이트하기도 힘들고 해서 한동안 방치했었는데, 더 이상 수동으로 관리하기가 귀찮아서 아예 자동으로 의존관계 매트릭스 엑셀파일을 만드는 프로그램을 하나 만들었다. 이름은 s3matrix.

스프링 소스를 받아서 ant publish-maven-local 을 실행하면 Ivy정보를 참조해서 Maven 로컬 리포지토리에 인스톨을 해준다. s3matrix를 실행하면 리포지토리에 설치된 스프링 pom 중에서 가장 최신 버전을 찾아서 그 dependecy 정보를 분석해서 엑셀파일 형태로 생성을 해준다.

이렇게 만든 의존관계 매트릭스 정보를 http://spreadsheets.google.com/ccc?key=ppDRa3Yit-05zS2cqWYFlNA 에 공유해놨다. 덕분에 라이브러리가 바뀔 때마다 쉽게 정보를 갱신 할 수 있게 되었다.

 

s3matrix를 만들면서 3.0에서 도입된 OXM 모듈을 사용해봤다. 엔진은 편리한 JAXB를 사용했다. xjc를 써서 스키마를 컴파일해 매핑 파일을 만들고 OXM의 unmarshaller를 세팅한후, 이를 이용해서 간단히 POM의 Model 오브젝트로 만들어 낼 수가 있다.

엑셀파일 생성은 기존에 사용하던 POI 말고 스프링에 새롭게 추가된 JExcelAPI를 사용했다. POI에 비해서 API도 깔끔하고 그 덕분에 코드도 간결해지고 성능도 더 좋은 듯하다. 앞으로 ExcelView는 JExcelAPI를 이용한 것으로 바꿔야 겠다.

최근에 1.1.30으로 업그레이드 된 JUnitMax를 사용해서 TDD로 개발했는데, TDD개발의 번거로움을 완벽하게 제거해주는 JUnitMax의 편리함에 계속 감동이다. 1.1.25이후에 추가된 상태바의 왼쪽에 최근에 수행한 테스트의 최종결과가 표시되는 기능도 맘에 든다. 실패한 테스트를 통과시키기 위해서 코드를 수정해 가다가, 상태바에 빨간색이 녹색으로 바뀌는게 보이면 패스. 어느 순간에 테스트가 동작하는지 거의 느끼지도 못할 정도로 자연스럽게 테스트가 진행된다.

특히 JUnitMax의 장점인 테스트 순서를 다이나믹하게 선택해주는 기술은 정말 매력적이다. 최근에 실패한 테스트가 있어서 그것을 성공시키기 위해 코드를 손을 보고 저장하면, JUnitMax가 알아서 가장  최근에 실패한 테스트를 우선적으로 수행해주기 때문에 작업한 코드에 대한 성공여부를 매우 빠르게 확인할 수 있다. 코드의 양이 늘어나고, 수행해야 할 테스트의 숫자가 늘어난다고 하더라도 지금 손보고 있는 코드와 관련된 테스트가 그 중에서 우선적으로 수행될 수 있기 때문에 전체 테스트가 다 돌아가기를 기다리는 불편도 없다.

이렇게 코드의 수정과 테스트가 거의 병렬도 수행되기 때문에 테스트를 수행하느라 코딩의 리듬이 끊기는 것을 막아주는 것이 TDD의 성공에 미치는 영향은 매우 크다고 생각된다. 이미 오래전부터 이런 방식의 테스트를 연구한 사람들이 있다.

http://blog.objectmentor.com/articles/2007/09/20/continuous-testing-explained 에 보면 MIT의 Program Analysis Group에서 이런 방식의 테스트를 Continuous Testing이라고 이름을 붙였고, 테스트와 관련되서 낭비되는 시간을 90% 이상 제거해준다는 연구결과도 내놓았다고 한다. TDD에 적용하기에 최고의 방법이다. 앞으로는 Continuous TDD 또는 CTDD라고 불러야겠다.

 

작업한 코드를 opensprout의 리포지토리에 올리긴 했는데 전체 공개로 권한 설정을 해놔도 자꾸 로그인 창이 뜨는게 뭔가 이상하다. 오래전에 설치한 구닥다리 Subversion을 좀 최신버전으로 업그레이드 하던가 Git으로 이 참에 아예 바꾸던가 해야겠다.

© 2017 Toby's Epril Suffusion theme by Sayontan Sinha