»
S
I
D
E
B
A
R
«
JUnit 4, 60초만에 익히기
Jul 3rd, 2011 by Wegra Lee

JUnit in Action 책 출간 기념으로 짤막한 글 하나 번역해본다.

원문: JUnit 4 in 60 Seconds

이번 주말에 JUnit 4 라이브러리를 살펴보고 간단히 정리해보았다.

1. @Test

테스트 케이스임을 명시하기 위해 사용한다. “test”와 같은 접두어를 붙일 필요가 없어졌다. 또한 테스트 클래스 역시 더 이상 “TestCase” 클래스를 확장할 필요가 없어졌다.

  1. @Test
  2. public void addition() {
  3. assertEquals(12, simpleMath.add(7, 5));
  4. }
  5. @Test
  6. public void subtraction() {
  7. assertEquals(9, simpleMath.substract(12, 3));
  8. }

2. @Before and @After

@Before와 @After 애너테이션을 사용하여 “setup”과 “testDown” 메서드 명시한다. 이 메서드들은 각각의 테스트 전후로 실행된다.

  1. @Before
  2. public void runBeforeEveryTest() {
  3. simpleMath = new SimpleMath();
  4. }
  5. @After
  6. public void runAfterEveryTest() {
  7. simpleMath = null;
  8. }

3. @BeforeClass and @AfterClass

@BeforeClass와 @AfterClass는 클래스 단위의 “setup”과 “teadDown” 메서드에 붙인다. 1회성 setup, tearDown 메서드라 생각하자. 클래스고 파홈한 모든 테스트케이스들의 전후로 단 1회만 실행된다.

  1. @BeforeClass
  2. public static void runBeforeClass() {
  3. // run for one time before all test cases
  4. }
  5. @AfterClass
  6. public static void runAfterClass() {
  7. // run for one time after all test cases
  8. }

4. Exception Handling (예외 처리)

예외 발생이 예상되는 테스트 케이스에는 @Test 애너테이션에 “expected” 파라미터를 사용한다. 발생해야할 예외의 클래스 명을 적어두면 된다.

  1. @Test(expected = ArithmeticException.class)
  2. public void divisionWithException() {
  3. // divide by zero
  4. simpleMath.divide(1, 0);
  5. }

5. @Ignore

무시하고자 하는 테스트 케이스에는 @Ignore 애너테이션을 붙인다. 무시하는 이유도 기입해 넣어주면 좋다.

  1. @Ignore(“Not Ready to Run”)
  2. @Test
  3. public void multiplication() {
  4. assertEquals(15, simpleMath.multiply(3, 5));
  5. }

6. Timeout (시간 제한)

제한 시간이 필요하면 “timeout” 파라미터를 이용한다. 단위는 밀리초이다. 테스트가 제한시간 내에 완료되지 못하면 실패 처리된다.

  1. @Test(timeout = 1000)
  2. public void infinity() {
  3. while (true)
  4. ;
  5. }

7. 새로운 단언들

배열 비교에 쓰일 수 있는 새 단언 메서드들이 추가되었다. 배열의 길이가 같고, 각 원소들이 대상 배열의 대응되는 원소들과 같아야(equal) 한다.

  • public static void assertEquals(Object[] expected, Object[] actual);
  • public static void assertEquals(String message, Object[] expected, Object[] actual);
  1. @Test
  2. public void listEquality() {
  3. List<Integer> expected = new ArrayList<Integer>();
  4. expected.add(5);
  5. List<Integer> actual = new ArrayList<Integer>();
  6. actual.add(5);
  7. assertEquals(expected, actual);
  8. }

8. JUnit4Adapter

JUnit 3에서 JUnit 4용 테스트를 실행하려면 JUnit4Adapter를 사용한다.

  1. public static junit.framework.Test suite() {
  2. return new JUnit4TestAdapter(SimpleMathTest.class);
  3. }

자! 그럼 모두 즐코딩~ ^^

[개발자 역량] 난 디버깅 킹왕짱
Sep 30th, 2009 by Wegra Lee

디버깅을 잘 한다.

나는 과거 종종 그런 얘기를 들은 적도 있고, 또 스스로도 제법 잘 한다고 생각한다. (최근 몇 년간은 직접적인 개발과는 거리가 좀 있는 업무들을 주로 수행해 왔다.)

그런데 ‘디버깅을 잘한다’는 말에는 어떤 의미가 포함되어 있을까.

아무 개념 없이 엉터리로 짠 남의 코드에서 산발적으로 발생하는  미지의 문제를 소스 코드만 추적해서 잘아낼 수 있으면 디버깅을 잘 하는 것일까? 분명 이런 능력은 대단한 것겠지만.. 현실적으로는 ‘운이 좋았다’ 이상을 기대하긴 어렵다. 주변에서도 능력 있는 개발자가 다른 프로젝트 합류 후 ‘내가 할 수 있는 일이 없다’고 푸념하고 경우를 몇 번 보았다. 이유는 몇 년 동안 들여다보던 사람이 아니면 도저히 손 댈 엄두가 안나는 코드를 사용하고 있기 때문이었다. 종속된 모듈과 프로젝트가 많고 덩치도 만만치 않아 새로 깔끔히 만들 수도 없다는 것이다. 동작하는 코드를 새로 만들 시간을 달라는 요청은 관리자들에게 쉽게 묵살당한다.

결국 아무리 능력 좋은 개발자도 대상 소프트웨어의 상태/환경에 따라 발휘되는 능력은 극심한 편차를 보이기 마련이다. 좀 더 정리하여 이야기하면, 커버할 수 있는 임계점 이하까지는 쉽게쉽게 문제를 찾아내지만, 그 한계를 넘어서면 거의 컨트롤이 불가하여 평범한 개발자와 별 차이를 보이지 못하게 된다. 이는 인간이 머릿속에 한 번에 담고 분석/추적할 수 있는 정보량이 그리 많지 않기 때문이다.

중요한 것은 디버깅 대상을 단순 명료하게 유지하는 능력이다. 또는 복잡한 대상을 단순하게 변형하는 능력이다. 구체적인 방법들을 떠올려보자.

  • 컨벤션을 잘 따른다. 예외 상황을 신경쓸 필요가 없으므로 개발자는 context 에 집중할 수 있게 된다.
  • 읽을 수 있는 코드를 작성한다. 해석하는 것과 읽는 것의 차이를 생각해보자.
  • Refactoring 을 통해 코드를 항시 명쾌하게 유지한다.
  • Testability 를 생각해 디자인한다. 문제가 명확하지 않다면 테스트를 통해 하나하나 문제 쉽게 좁히기 쉽다. 디자인이 잘 되어 있다면 모듈 하나하나를 분리해 독립적으로 검증할 수도 있을 것이다. TDD 같은 실천법을 활용하는 것도 적극 추천한다.

이상은 순수한 소스 코드 & 설계의 관점에서의 디버깅 능력 향상 방법들이었다. 유지보수를 위한 지침과 동일하다고 볼 수 있다. 그럼 다른 관점에서는 또 어떤 방법들이 있을까?

  • 다양한 결함 분석 툴을 활용해 잡을 수 있는 문제들을 미리 잡아 놓는다. 사소한 문제들, 혹은 툴이 잘 잡아주는 결함들을 미리 제거해 두면 잠재한 문제의 범위가 좁혀지고, 그만큼 더 집중할 수 있게 된다. (디버깅은 범죄 수사와 마찬가지로, 문제 범위를 좁히고 그에 집중해 파헤쳐보고, 잘못 집었다 싶으면 되돌아가 다른 가능성에 집중에 파헤쳐보는 스타일로 진행된다.)
  • 평소의 좋은 로깅 습관이 디버깅을 높기도 한다. 더욱 효율적인 로깅을 원한다면 역시 좋은 로깅 솔루션을 도입하는 것이 좋다. 주변에 찾아보면 다양한 로깅 프레임워크들을 쉽게 구할 수 있을 것이다. 이들은 로그를 다양한 목적에 맞게 구분짓고, 상황에 맞게 조작/필터링 등을 쉽게 할 수 있도록 도와준다.
  • 디버깅 툴의 파워 유저가 되자. 이러저러한 모든 것을 만족해도 풀지 못하는 문제가 있을 수 있다. 또는 어쩔 수 없이 좋지 않는 코드를 디버깅해야 하는 경우도 많다. 따라서 디버깅 툴을 확실히 활용할 수 있는 능력은 기본으로 갖춰두어야 한다.
  • 개발툴/언어 외적인 보조 방법을 활용한다. 예를 들어 aspect 언어를 활용할 줄 안다면 디버거로 쉽게 잡아내지 못하는 복잡한 상황까지도 아주 간단히 연출해낼 수 있다.

이상은 소스 코드 외적인 방법이었지만, 개인이 충분히 할 수 있는 것들이다. 그럼 마지막으로 소셜 개발자라면 또 어떠한 방법들을 생각해볼 수 있을지 나열해보겠다.

  • 유사 도메인의 구루, 혹은 전문 커뮤니티 활용한다.
    오늘날 대부분의 프로젝트는 다수의 외부 라이브러리/플랫폼을 활용하기 때문에, 이 능력의 중요성은 과거보다 훨씬 커졌다. 내가 겪는 대부분의 문제는 누군가 이미 겪어서 그 해결책(혹 ‘어쩔 수 없다’일지라도)이 공개되어 있다. 대답을 찾지 못하더라도 질문을 올리면 늦어도 몇 일 내에 만족할만한 답을 찾을 수 있을 것이다.
    테스트 방법이나 설계에 대한 조언도 쉽게 구할 수 있다. 물론 앞서의 경우보다는  훨씬 복잡한 이슈라 자신이 처한 상황을 명확히 기술해주어야 좋은 답을 얻을 가능성이 그만큼 높아진다.
    중요한 부분이 아니라면 소스 코드를 공개해
  • 만든 제품에 대해 피드백을 받을 수 있는 커뮤니티를 만들고, 가능하다면 오픈소스화 한다.
    직접 디버깅 하는 것만이  꼭 능력은 아니다. 더 궁극적인 목적은 결함 없는 제품을 만드는 것이 아닌가? 그러려면 프로젝트가 남의 관심을 받을 만큼 충분히 유용해야하고, 그들의 요구사항을 적극 반영하며 기여자(contributor)들과 지속적으로 좋은 관계를 유지해야 한다.

디버깅 하나 얘기하면서 참 많은 것을 훑었다. 오버라고 생각할 수도 있겠지만, 개발이라는 것이 그리 딱딱 떨어지는 것이 아니고.. 이것이 현실이다. 오히려 위에 나열할 것 외에도 수많은 요소들이 훌륭한 디버거를 만드는 관여될 것이 분명하다.

자!! 그럼 지금까지 나온 좋은 디버거로서의 자질은 정리해보자.

  • 명확하고 논리 정연한 코드 작성 능력
  • 꾸준한 코드 관리
  • Testable 디자인 능력
  • 다양한 분석 툴 활용 능력
  • 좋은 로깅 습관 & 로깅 툴 활용 능력
  • 디버깅 툴 활용 능력
  • 주변 전문가, 커뮤니티, 웹 상의 정리된 지식 활용 능력
  • 외부 기여자를 끌어모아 적극적인 피드백을 이끌어 내는 능력
TDD, Refactoring and Scrum – Part II
Sep 9th, 2009 by Wegra Lee

내가 지금까지 접해본 여러 이론/실천법 들을 통틀어 가장 맘에 드는 것들을 세 가지 고르라면 TDD, Refactoring, Scrum, 이렇게 세 가지를 뽑겠다. (우리 조직에선 이 중 단 하나도 하지 않고 있어서 참으로 답답하고 안타깝다.) 다른 사람들과는 좀 다른 관점일 수 있겠는데, 이들에 애착이 생기는 내 나름의 이유는 간략히 정리해본다.

  • Part I – TDD
  • Part II – Refactoring (this article)
  • Part III – Scrum (not available yet)

Refactoring – Refactoring은 현재의 잘못된 점이나 부족한 부분을 식별하여 더 나은 형태로 개선하는 작업이다. 따라서 Refactoring 은 일종의 회고이며, 그 주된 관점은 설계/코드의 효율성, 가독성, 명확성 등이다. 사람이나 조직이 회고를 통해 교훈을 얻고 성장하듯, 개발자도 Refactoring 을 통해 역량을 키워나간다. 여건이 되지 않으면 혼자서라도 틈틈히 수행해야 하며, 다행히 지도해줄 경험 많은 동료가 함께한다면 그 효과는 배가된다. 팀의 리더라면 팀원간의 이런 교류가 자연스레 일어날 수 있도록 이끌어 주어야 한다.

Refactoring은 개발 기간 내내 리듬을 가지고 정기적으로 수행되어야 한다. 리펙토링 문화가 충분히 자리잡지 못했다면 프로세스에 명시해두는 것이 좋다. 시점은 매 반복 주기(iteration/milestone)가 끝나고 다음 주기가 시작되는 시점이다. 새로운 기능을 넣기 전에 기반을 단단히 다진다거나, 구조적 문제를 조기에 다잡아 추후 대규모 수정을 예방한다는 점도 물론 중요하지만, 사람(개발자)의 역량 성장 측면에서도 반드시 요구되는 프로세스이다.

배우고 경험하고 배우고 경험하고.. 두 과정이 주기적으로 반복되어야 머리로 익힌 것이 체화되고, 그것이 실생활에서 어떤 가치가 있는지 진정한 의미를 깨닫게 된다. 처음에 좋아보였던 것도 생각지 못했던 숨은 이슈나 과소 평가했던 측면 때문에 실제로는 역효과가 더 큰 경우도 많다. 또한 체험 없이 계속해서 머리로만 익히다 보면 모든 것이 쉽고 단순해 보이는 경향이 있다. 이렇게 해서 다음 리펙토링을 수행할 때에는 이전 수행 시보다 향상된 시각으로 구조를 바라볼 수 있게 된다. 설계와 코드를 보는 눈이 향상되고, 시행착오가 줄어들고, 다양한 경험을 통해 확신을 가질 수 있다. 주기적인 Refactoring은 프로젝트가 진행될 수록 Refactoring의 품질과 그것을 수행하는 개발자 역량을 함께 성장시킬 수 있는 멋진 제도적 장치가 될 것이다.

Refactoring 시 주의점

리펙토링 시 가장 신경써야할 부분은 역시 side effect 다. 때문에 전문 툴 활용과 풍부한 테스트 케이스가 준비가 큰 도움을 준다. 툴의 Refactoring 지원 정도는 언어별로 편차가 심하다. Small Talk, Java 언어는 Refactoring 이 지원되지 않는 툴은 오래전에 자취를 감췄을 정도로 대중화되어 있고, C#, Visual Basic 등은 중간 정도.. C/C++ 계열은 아직 많이 부족하며 발전 속도도 더디다.

툴이 해주는 일은 다양한 리펙토링 기법들을 마우스 클릭 몇 번으로 관련된 모든 코드를 자동 변경해주는 편리함과, 이 때 발생할 수 있는 구조적 충돌을 사전 검증하여 side effect 을 최소화시켜주는 효과가 있다. 소프트웨어 규모가 커지면 수십명이 일주일 동안 해야할 변경량을 한 사람이 반나절 만에 처리할 정도로 극적인 효과를 볼 수도 있다.

툴이 구조적인 side effect를 예방해준다면, 테스트 케이스는 기능적인 side effect 를 예방해준다. 테스트 케이스가 충분치 않다면 변경의 잘못된 영향을 먼 훗날에야 발견하게 되고, 무엇이 그 원인이었는지 파악하기 어렵게 된다.

Refactoring 과 TDD
테스트의 대표 주자는 역시 TDD 이다. 현실적으로 거의 불가능하지만, TDD 를 100% 잘 따랐다면 작성된 코드 중 테스트 케이스가 커버하지 못하는 영역은 존재하지 않는다. 따라서 Refactoring 을 가장 안전하게 하기 위한 좋은 지침은 TDD 를 잘 따르는 것이다. 역으로 TDD 의 과정에서 새로운 테스트 케이스(요구사항)가 추가되면, 그 케이스를 만족시키기 위해 기존 코드를 Refactoring 해야한다. 이렇든 이 둘은 거의 바늘과 실 관계이다. 다만 Refactoring 없는 TDD 는 그 효과가 급감하지만, TDD 없이도 테스트 케이스는 작성할 수 있으므로 Refactoring 은 독자적으로도 충분한 가치가 있다.

TDD, Refactoring and Scrum – Part I
Aug 31st, 2009 by Wegra Lee

내가 지금까지 접해본 여러 이론/실천법 들을 통틀어 가장 맘에 드는 것들을 세 가지 고르라면 TDD, Refactoring, Scrum, 이렇게 세 가지를 뽑겠다. (우리 조직에선 이 중 단 하나도 하지 않고 있어서 참으로 답답하고 안타깝다.) 다른 사람들과는 좀 다른 관점일 수 있겠는데, 이들에 애착이 생기는 내 나름의 이유는 간략히 정리해본다.

TDD (Test Driven Development) – 보통 언급은 되지만 크게 부각되지 않는 장점. 종종 설명에서 아예 빠지기도 한지만, 내가 가장 높게 쳐주는 TDD의 장점은 바로 개발자들의 설계 능력의 향상’이다. 테스트는 곧 사용자의 사용 패턴을 시뮬레이션 한 것으로, 쉽게 테스트하기 위한 고민은 바로 쉽게 사용하기 위한 인터페이스를 고민하는 것이다. 이 과정을 몇 개월만 반복하더라도 테스트하기 쉬운 설계(testable design)와 그 반대의 케이스의 대표적인 사례(testable design anti-patterns)들을 한 다발 발견해낼 수 있을 것이다. 디자인 공부한답시고 UML 다이어그램 책이나 디자인 패턴 책을 열심히 파는 것보다, 자신이 개발중인 코드를 보고 테스트를 어떻게 할까를 고민해보는 것이 훨씬 효과만점이라고 자신있게 주장할 수 있다.

업계에선 이 부분이 잘 수행되지 않고 있는데, ‘테스트 == 설계’ 라는 관계가 잘 부각되지 않기 때문으로 보인다. (아쉽게도 팀원의 역량 향상을 등한시하는 매니저도 제법 있다.) 설계는 중요시하면서도 테스트는 등한시 하는 것이다. 개발자에게 설계를 잘 하라면서도 테스트는 외부 인력에 의존하는 경우가 너무 많다. 테스터들도 자신이 설계를 검증해야 한다는 것을 자각하지 못하고 있고, 혹 있다손 치더라도 설계에 대해 이러쿵 저러쿵 하는 것을 개발팀에서 그리 달가워하지 않는 경우도 종종 있다. 그리고 테스트를 너무 늦게 시작해 설계 문제가 드러나도 일정상 수정하지 못하는 안타까운 상황도 흔히 발생한다.

기본적인 테스트(유닛, 인티그레이션)는 가능한 모두 개발자들 스스로 수행하도록 하고, 외부 인력은 신뢰성, 고가용성, 보안, 스레드 안정성 등 전문 분야에 대해서 검증을 수행해 주는 것이 효율적이다. 개발자 테스트 단계에서도 단순 기능 검증이 아닌 테스트 용이성에 큰 비중을 주도록 꼭! 주지시켜야 한다.

이렇게 훈련된 개발자들은 나중에 혹 테스트 케이스를 작성할 시간이 부족하더라도 처음부터 상대적으로 수준 높은 설계를 만들어낸다. 신입사원들에게 별 쓸모 없는 프로세스나 테크닉 교육은 대폭 줄이고 TDD 나 한 두 달 시켜주면 현업에 훨씬 도움이 될 것이다. 대학과 확원에서도 이론과 테크닉에만 치우지지 말고 이런 실질적인 교육과 훈련에 좀 더 투자를 해야 한다.

다시 한 번 강조하는 것은 ‘개발자‘의 역량 향상이다. 그 효과는 영구적이고 사기 증진에도 많은 도움이 된다. 제품의 설계 향상 같은 단기적인 이점은 그에 비해 훨씬 가치가 적다.

TDD 에 관심이 있는 사람이라면 BDD (Behavior Driven Development – Click Here) 도 한 번쯤 꼭 참고해보도록 하자. TDD 의 발전 형태로 볼 수 있는데, 무엇을 어떤 식으로 테스트해야할 지에 대한 좋은 가이드라인을 제시해준다. 개념을 확실히 인지하고 있다면 전용 툴 지원  없이 전통적인 Unit Test Framework 만으로도 BDD 를 활용할 수 있다.

단순 API 테스트라면 boundary value analysis (Click Here) 와 equivalence partitioning (Click Here) 정도는 확실히 알아두자. 이 둘을 조합하면 많은 경우에 있어 기계적으로 test case 들을 뽑아낼 수 있다. SE, SQA, 혹은 전문 테스터로서 역량을 원한다면 이곳 (Click Here) 도 좋은 레퍼런스가 될 것이다.

»  Substance: WordPress   »  Style: Ahren Ahimsa