두가지 대표적인 프로그래밍 스타일

Posted by epicdev Archive : 2011. 11. 10. 13:03
어떤 문제를 풀어야 할 때 머릿속으로 완벽한 해답이 나올 때까지 생각만 하다가 해답이 나오면 구현을 시작하는 스타일과
일단 완벽한 해답이 나오지 않더라도 불완전한 해답만 가지고 구현부터 시작한 다음 서서히 고치면서 해답을 만들어가는 스타일이 있는데
이 둘을 적재적소에 잘 사용해야 한다. 
  

개발의 기본 흐름

Posted by epicdev Archive : 2011. 11. 10. 12:37
개발의 기본 흐름은 일단 실패하는 테스트를 작성하고, 그 다음으로 그 테스트를 통과하도록 만드는 것이다. 이 흐름은 여러 다른 규모에서도 그대로 작용한다.
분기 단위에서는, 해결하고 싶은 주제들을 목록으로 만들고 그걸 다시 스토리 여러 개로 만들어 해결한다.
일주일 단위에서는, 해결하고 싶은 스토리들을 목록으로 만들고, 그 스토리들을 표현하는 테스트들을 작성하고, 그런 다음 그 테스트들을 통과하도록 만든다.
몇 시간 단위에서는, 여러분이 작성해야 할 필요가 있다고 생각하는 테스트들을 목록으로 만들고, 테스트를 하나 작성하고, 그 테스트를 통과하도록 만들고, 다른 테스트를 작성하고, 두 테스트 모두 통과하도록 만들고 하면서 목록이 비워질 때까지 일한다.
 

익스트림프로그래밍(ExtremeProgramming)
카테고리 컴퓨터/IT > 컴퓨터공학
지은이 켄트 벡 (인사이트, 2006년)
상세보기
 
  

일에 대한 만족감

Posted by epicdev Archive : 2011. 11. 8. 23:03
자신을 보호하기 위해 뭔가를 유보해두는 오래된 습관은 사실 효과가 없다. 마지막 20%의 노력을 쓰지 않고 남겨두는 것이 나를 지켜주지는 않는다. 프로젝트가 실패한다면, 내 모든 것을 그 프로젝트에 다 쏟아 붓지 않았다고 해서 내 기분이 좋아지지는 않는다. 프로젝트를 성공시킬 수 없었다는 실패감에서 나를 보호해주지는 않는다. 하지만 최선을 다해 프로그램을 작성했는데도 사람들이 그 프로그램을 좋아하지 않는다면, 나는 여전히 자신에 대해 만족감을 느낄 수 있다. 이런 태도를 취한다면 상황이 어떻든 안전함을 느낄 수 있다. 내가 어떻게 느끼는지가 내가 최선을 다했느냐 아니냐에 달려 있다면, 최선을 다하기만 한다면 언제나 자신에 대해 만족감을 느낄 수 있기 때문이다.


익스트림프로그래밍(ExtremeProgramming)
카테고리 컴퓨터/IT > 프로그래밍/언어
지은이 켄트 벡 (인사이트, 2006년)
상세보기
 

'Archive' 카테고리의 다른 글

두가지 대표적인 프로그래밍 스타일  (0) 2011.11.10
개발의 기본 흐름  (0) 2011.11.10
Viola-Jones Face Detection Visualization  (0) 2011.10.22
5 Programming Languages Everyone Should Know  (0) 2011.10.18
MLE vs. MAP  (0) 2011.10.16
  

Viola-Jones Face Detection Visualization

Posted by epicdev Archive : 2011. 10. 22. 17:57
  

5 Programming Languages Everyone Should Know

Posted by epicdev Archive : 2011. 10. 18. 18:24

Larry Wall: Javascript, Java, Haskell, C, Perl (or Python or Ruby)



Bjarne Stroustrup: C++, Java, Python
  

MLE vs. MAP

Posted by epicdev Archive : 2011. 10. 16. 04:44
출처: http://masanjin.net/blog/bayes-vs-mle

 Bayes vs MLE: an estimation theory fairy tale

I found a neat little example in one of my introductory stats books about Bayesian versus maximum-likelihood estimation for the simple problem of estimating a binomial distribution given only one sample.
 

I was going to try and show the math but since Blogger is not making it possible to actually render MathML I’ll just hand-wave instead. [Fixed in Whisper. —ed.]
 

So let’s say we’re trying to estimate a binomial distribution parameterized by p, and that we’ve only seen one estimate. For example, someone flips a coin once, and we have to decide what the coin’s probability of heads is.
 

The maximum likelhood estimate for p is easy: if your single sample is a 1, then p=1, and if your sample is 0, p=0. (And if you go through the laborious process of writing the log likelihood, setting the derivative equal to 0, and solving it, you come up with the general rule of (# of 1’s) / (# of 1’s + # of 0’s), which is kinda what you would expect.)
 

In the coin case it seems crazy to say, I saw one head, so I’m going to assume that the coinalways turns up heads, but that’s because of our prior knowledge of how coins behave. If we’re given a black box with a button and two lights, and you press the button, and one of the lights come on, then maybe estimating that that light always comes on when you press the button makes a little more sense.
 

Finding the Bayesian estimate is slightly more complicated. Let’s use a uniform prior. Our conditional distribution is f(1|p)=p and f(0|p)=1p, and if you work it out, the posterior ends up as h(p|1)=2p and h(p|0)=2(1p).
 

Now if we were in the world of classication, we’d take the MAP estimate, which is a fancy way of saying the value with the biggest probability, or the mode of the distribution. Since we’re using a uniform prior, that would end up as the same as the MLE. But we’re not. We’re in the world of real numbers, so we can take something better: the expected value, or the mean of the distribution. This is known as the Bayes estimate, and there are some decision-theoretic reasons for using it, but informally, it makes more sense than using the MAP estimate: you can take into account the entire shape of the distribution, not just the mode.
 

Using the Bayes estimate, we arrive at p=2/3 if the sample was a 1, and p=1/3 if the sample was a zero. So we’re at a place where Bayesian logic and frequentist logic arrive at very different answers, even with a uniform prior.
 

Up till now we’ve been talking about “estimation theory”, i.e. the art of estimating shit. But estimation theory is basically decision theory in disguise, where your decision space is the same as your parameter space: you’re deciding on a value for p, given your input data, and your prior knowledge, if any.
 

Now what’s cool about moving to the world of decision theory is that we can say: if I have to decide on a particular value for p, how can I minimize my expected cost, aka my risk? A natural choice for a cost, or loss, function, is squared error. If the true value is q, I’d like to estimate p in such a way that E[(qp)2] is minimized. So we don’t have to argue philosophically about MLEversus MAP versus minimax versus Bayes estimates; we can quantify how well each of them do under this framework.
 

And it turns out that, if you plot the risk for the MLE estimate and for the Bayes estimate under different values of the true value q, then MOST of the time, the Bayes estimate has lower risk than the MLE. It’s only when q is close to 0 or to 1 that MLE has lower risk.
 

So that’s pretty cool. It seems like the Bayes estimate must be a superior estimate.
 

Of course, I set this whole thing up. Those “decision-theoretic reasons” for choosing the Bayes estimate I mentioned? Well, they’re theorems that show that the Bayes estimate minimizes risk. And, in fact, the Bayes estimate of the mean of the distribution is specific to squared-error loss. If we chose another loss function, we could come up with a potentially very different Bayes estimate.
 

But my intention wasn’t really to trick you into believing that Bayes estimates are awesome. (Though they are!) I wanted to show that:

  1. Bayes and classical approaches can come up with very different estimates, even with a uniform prior.
  2. If you cast things in decision-theoretic terms, you can make some real quantitative statements about different ways of estimating.


In the decision theory world, you can customize your estimates to minimize your particular costs in your particular situation. And that’s an idea that I think is very, very powerful.
 

— William MorganOctober 7, 2008.
  
nslookup 명령어를 사용

'Archive' 카테고리의 다른 글

5 Programming Languages Everyone Should Know  (0) 2011.10.18
MLE vs. MAP  (0) 2011.10.16
Exception에 관하여  (0) 2011.10.08
가차 없는 테스트  (0) 2011.10.06
다익스트라의 테스팅 관련 명언  (0) 2011.10.06
  

Exception에 관하여

Posted by epicdev Archive : 2011. 10. 8. 17:23
출처: http://babtingdev.tistory.com/302

 try { 
   ...
} catch (SQLException e) {} 

보통 예외를 잡고는 아무것도 하지 않는 이러한 코드를 많이 작성한다. 예외 발생을 무시해 버리고 정상적인 상황인 것처럼 다음 라인으로 넘기겠다는 분명한 의도가 있는게 아니라면 연습 중에도 절대 만들어서는 안되는 코드이다.
이것은 예외가 발생하는 것보다도 훨씬 안좋은 상황을 만든다. 오류가 있는 건데 오류를 무시하고 계속 진행해버리기 때문이다.

try {
   ...
} catch(SQLException e) {
   System.out.println(e);
}

try {
   ...
} catch(SQLException e) {
   e.printStackTrace();
}

위 두개도 마찬가지로 사용해선 안될 코드들 이다. 예외는 처리되어야 한다.

예외를 처리할 때 반드시 지켜야 할 원칙은 한가지 이다. 모든 예외는 적절하게 복구되던지 아니면 작업을 중단시키고 운영자 또는 개발자에게 분명하게 통보되야 한다.

무의미하고 무책임하게 throws를 사용하는 개발자들도 있다. 처리하기 귀찮으니 그냥 메소드 선언부에 throws 를 써서 넘겨버리는 것이다. (아래 코드와 같이.)
public void method1() throws Exception {
}
try~catch로 Exception을 삼켜버리는 코드보다야 낫지만 무작정 throws 를 남발하는 이 방법도 매우 안좋은 방법이다. 


예외의 종류와 특징에 대해 알아보자.
1. Error 
java.lang.Error 클래스의 서브클래스들이다. 에러는 시스템에 뭔가 비정상적인 상황이 발생했을 경우에 사용된다. 그래서 주로 자바VM에서 발생시키는 것이고 어플리케이션 코드에서 잡으려고 하면 안된다. OutOfMemoryError나 ThreadDeath같은 에러는 catch블록으로 잡아봤자 대응 방법이 없기 떄문이다.

2. Exception
java.lang.Exception 클래스와 그 서브클래스로 정의되는 예외들은 개발자들이 만든 어플리케이션 코드의 작업중에 예외상황이 발생했을 경우에 사용된다.
Exception은 체크예외와 언체크 예외로 구분된다. 언체크 예외는 Exception의 서브클래스이며 RuntimeException클래스를 상속받은 클래스이며 체크 예외는 RuntimeException클래스를 상속받지 않은 나머지 Exception의 서브클래스들이다.

2.1 체크예외
체크예외는 무조건 처리되어야 하는 예외이다. try~catch, throws를 통해 어떻게든 처리를 하지 않으면 컴파일 에러가 난다.
IOException이나 SQLException이 대표적이다.

2.2 언체크/런타임 예외
java.lang.RuntimeException 클래스를 상속한 예외들이며 명시적인 예외처리를 강제하지 않는다. (try~catch, throws등의 처리를 안해도 컴파일 에러가 안난다는 의미.)
물론 명시적인 처리를 하는 것도 가능하다. 
런타임 예외는 주로 프로그램의 오류가 있을 때 발생하도록 의도된 것이다. NullPointerException이나 IllegalArgumentException등이 대표적이다. 피할 수 있지만 개발자가 부주의 해서 발생할 수 있는 경우에 발생하도록 만든 것이 런타임 예외이다. 따라서 런타임 예외는 예상하지 못했던 예외상황에서 발생하는게 아니기 떄문에 명시적인 처리를 하지 않아도 되게 만든 것이다.


이제 예외를 처리하는 방법에 대해 알아보자.

1. 예외복구
예외상황을 파악하고 문제를 해결해서 정상상태로 돌려놓는 것이다. 예를 들면 읽으려는 파일이 없을때 IOException이 발생할 것이다. 이 예외가 발생한 상황을 다른 파일을 읽도록 안내하는 걸로 해서 예외상황을 해결할 수 있다.

2. 예외처리 회피
예외처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 것이다. 
public void add() throws SQLException {
}

public void add() throws SQLException {
   try {
      ...
   } catch(SQLException e) {
      throw e;
   }
}
하지만 이 방법은 무책임한 책임회피가 될 수 있다. 예를 들어 DAO가 SQLException을 생각없이 던졌다면?
DAO를 사용하는 서비스 계층이나 웹 컨트롤러에게 이 SQLException이 전달 될 것이다. 과연 서비스 계층이나 웹 컨트롤러 계층에서 이 SQLException을 처리하는게 맞는 것인가? 처리는 할 수 있나? 아마 이 예외는 그냥 처리되지 않고 서버로 던져지게 될 것이다.
예외를 회피하는 것은 예외를 복구하는 것처럼 의도가 분명해야 한다. 예외를 회피하는 것은 자신을 호출해서 사용하는 쪽에서 이 예외를 다루는게 최선의 방법이라는 확신이 있을 경우에만 사용하도록 한다.

3. 예외 전환
예외 회피와 비슷하게 예외를 복구해서 정상적인 상태로 만들 수 없는 경우 예외를 메소드 밖으로 던진다. 하지만 예외회피와는 달리 발생한 예외를 그냥 던지는 것이 아니라 적절한 예외로 전환해서 던진다.
첫째는 내부에서 발생한 예외를 그대로 던지는 것이 그 예외 상황에 대한 적절한 의미를 부여해주지 못하는 경우, 의미를 분명하게 해줄 수 있는 예외로 바꿔주기 위해서이다.
예를 들면 사용자를 DB에 insert하다가 동일한 id가 있어 에러가 났다고 하자. JDBC API는 SQLException을 발생시킨다. 이 경우 DAO가 그냥 SQLException을 던지면 이를 이용하는 서비스 계층에서는 무슨 의미인지 파악하기 힘들 것이다. 이런 경우 SQLException을 DuplicatUserIdException 같은 예외로 바꿔서 던져주는게 좋다.

catch(SQLException e) {
   if(e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY)
      throw DuplicateUserIdException();
   else 
      throw e;
}

보통 예외를 전환해서 던져줄때에는 원래 발생한 예외를 담아서 중첩예외로 만드는 것이 좋다.
중첩 예외로 만들었을 경우에는 getCause() 메소드를 이용해서 처음 발생한 예외가 무엇인지 확인할 수 있기 때문이다.
생성자나 initCause()메소드로 근본 원인이 되는 예외를 넣어주면 된다.

catch(SQLException e) {
    throw DuplicationUserIdException(e);
}

catch(SQLException e) {
    throw DuplicationUserIdException().initCause(e);
}

예외전환을 사용하는 경우는 위의 경우도 있지만 주로 예외처리를 강제하는 체크 예외를 예외처리를 강제하지 않는 런타임 예외로 바꾸는 경우에 사용한다.
DAO에서 발생한 SQLException이 웹 컨트롤러까지 전달이 된다고 해서 무슨 소용이 있을까? 웹 컨트롤러의 메소드에 throws SQLException이 선언되어 있다면 이를 어떻게 해석해야 할까? 어차피 복구가 불가능한 예외라면 가능한 빨리 런타임 예외로 포장해 던지게 해서 다른 계층의 메소드를 작성할 때 불필요한 throws 선언이 들어가지 않게 해야한다. DAO에서 발생한 SQLException이지 웹컨트롤러와는 사실 상관이 없는 예외이기 때문에 헷갈릴만한 상황을 만들면 안된다.
어차피 복구하지 못할 예외라면 어플리케이션 코드에선 런타임 예외로 포장해서 던지고, 예외처리 서비스 등을 이용해 자세한 로그를 남기고, 관리자에게 메일을 전송하고 사용자에겐 안내메시지를 보여주는 것도 처리 방법이다.


자바가 처음 만들어 질때는 AWT, Swing 등을 사용해서 독립형 어플리케이션을 개발했었다. 이때는 사용자가 입력한 이름에 해당하는 파일을 찾을 수 없다고 어플리케이션이 종료가 되면 안되었었다. 어떻게든 처리를 했어야 했다.
하지만 자바 엔터프라이즈 서버 환경은 다르다. 수많은 사용자가 요청을 보내고 각 요청들이 독립적인 작업으로 취급된다. 요청을 처리하다 예외가 발생하면 해당 작업만 중단시키면 된다. 독립형 어플리케이션과는 달리 서버의 특정 계층에서 예외가 발생했을 때 작업을 일시 중지하고 사용자와 바로 커뮤니케이션하면서 예외상황을 복구할 수 있는 방법은 없다.

최근에 등장하는 표준스펙 또는 오픈소스 프레임웍에서는 API가 발생시키는 예외를 체크예외 대신 언체크 예외로 정의하는 것이 일반화되고 있다. 예전에는 복구할 가능성이 조금이라도 있다면 체크예외로 만든다고 생각했는데 지금은 항상 복구할 수 있는 예외가 아니면 일단 언체크/런타임 예외로 만드는 경향이 있다.

체크예외를 언체크/런타임 예외로 만드는걸 구현해보자. 대표적인 체크예외가 SQLException이다. 그 중 동일한 pk를 
입력했을때 에러가 발생해서 SQLExcepion이 나는 상황을 구현해 보겠다.

일단  동일한 pk가 입력이 되었을 경우 사용할 RuntimeException을 상속받은 DuplicateKeyException 을 만들어 보자.
public class DuplicateKeyException extends RuntimeException {
   public DuplicateKeyException(Throwable cause) { // 중첩 예외를 만들기 위해 생성자를 만들어 이용.
      super(cause);
   }
}

이제 add() 메소드를 구현해 보겠다.
public void add() throws DuplicateKeyException {
    try {
         ...
    } catch(SQLException e) {
         if(e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY) // Mysql 일 경우.
              throw new DuplicateKeyException(e); // 예외 포장
         else 
              throw new RuntimeException(e);
    }
}

RuntimeException을 사용하게 되면 꼭 예외 처리가 필요한 경우는 놓칠 수도 있게 된다. 반드시 예외를 catch해서 조치를 취하도록 요구하는 예외를 일반적으로 어플리케이션 예외라고 한다.
흔히 에러가 났을 경우 처리하는 작업이 특정 코드값을 정해서 리턴하는 방법이 있다. 
0이면 에러, 1이면 정상 이런 식으로...
이 방법은 각 코드값들을 명확하게 정리를 잘 해놓지 않으면 흐름을 파악하고 이해하기 어려워진다.
두번째 방법은 오류 발생시 비즈니스적인 의미를 띈 예외를 던지도록 하는 것이다. 물론 각 예외들은 위에 설명한 중첩방식같이 별도로 구현해서 사용을 해야 한다. 이 방법은 정상적인 흐름은 try {}안에 모아놓고 예외상황에 대한 코드들은 catch{}로 분리시킴으로 인해 깔끔하게 정리가 된다. 그리고 예외의 이름만 봐도 코드가 이해하기 편해지는 장점이 있다.
  

가차 없는 테스트

Posted by epicdev Archive : 2011. 10. 6. 21:01
개발자 대부분은 테스트를 싫어한다. 코드가 어디에서 깨지는지 무의식적으로 알고 약한 지점을 피해 다니면서, 살살 테스트하려 한다. 실용주의 프로그래머들은 다르다. 우리는 지금 당장 버그를 찾아 나서도록 내몰리지만, 그 대신 나중에 다른 사람이 자기 버그를 발견하게 되는 수치를 피할 수 있는 것이다.

버그 찾기는 그물낚시와 비슷하다. 잔챙이를 잡기 위해 촘촘한 그물(단위 테스트)을 쓰기도 하고, 식인상어를 잡기 위해 크고 성긴 그물(통합 테스트)을 쓰기도 한다. 때때로 고기가 용케 도망가기도 한다. 그렇게 되면 프로젝트 웅덩이에서 헤엄쳐 다니는 미끌미끌한 결함들을 많이많이 잡기 위해 구멍 난 데를 있는 대로 찾아다니며 막아야 하는 것이다.

<실용주의 프로그래머 팁>
일찍 테스트하고, 자주 테스트하라. 자동으로 테스트하라.

코드를 작성하자마자 테스트해야 한다. 그 작은 잔챙이들은 꽤나 빨리 자라나 사람을 잡아먹는 거대한 상어가 되는 고약한 성질이 있다. 상어를 잡는 일은 상당히 힘들다. 하지만 그렇다고 그 모든 테스트를 손으로 할 수는 없다.

프로젝트 테스트 계획을 상세하게 짜는 팀도 많다. 심지어 그걸 쓰는 팀도 간혹 있다. 하지만 우리가 보기에 자동화 된 테스트를 사용하는 팀이 성공의 기회가 훨씬 많다. 빌드 할 때마다 하는 테스트는 책장에 꽂아 놓은 테스트 계획보다 훨씬 효과적이다.

버그가 빨리 발견 될수록 고치는 비용이 적어진다. '코드 조금, 테스트 조금'은 스몰토크 세계에서는 유명한 격언이다. 우리는 제품 코드를 만드는 것과 동시에(혹은 이전에) 테스트 코드를 만듦으로써 그 주문을 우리 것으로 할 수 있다.

사실, 훌륭한 프로젝트에는 제품 코드보다도 테스트 코드가 더 많을지 모른다. 테스트 코드를 만들기 위해 소요되는 시간에는 그 노력만큼의 가치가 있다. 길게 보면 이쪽이 훨씬 더 싸게 들며, 결함이 영어 가까운 제품을 만드는 꿈이 정말 이루어지기도 한다.

이 외에도 테스트를 통과했다는 것은 그 코드가 '완료되었다'고 말할 수 있는 높은 수준의 확신을 갖게 하는 것이다.

<실용주의 프로그래머 팁>
모든 테스트가 통과하기 전엔 코딩이 다 된 게 아니다.

우리는 프로젝트 범위에서 이루어지는 테스트의 세 가지 주요 면모를 살펴 보아야 한다. 무엇을 테스트 할지, 어떻게 테스트 할지, 그리고 언제 테스트할지.


무엇을 테스트할지
 

수행해야 할 소프트웨어 테스트에는 대여섯 가지 주요 유형이 있다.

  • 단위 테스트
  • 통합 테스트
  • 유효성 평가와 검증
  • 자원 고갈, 에러, 그리고 복구
  • 성능 테스트
  • 사용 편의성 테스트

어떻게 테스트할지

  • 회귀 테스트
  • 테스트 데이터
  • GUI 시스템 구동
  • 테스트를 테스트하기
  • 철저히 테스트하기

테스트 데이터
 

여기에는 오직 두 종류의 데이터가 있다. 실세계 데이터와 합성 데이터다. 실제로는 이 둘을 모두 사용해야 하는데, 두 데이터가 갖는 다른 특징들이 소프트웨어에서 다른 종류의 버그를 노출시켜 주기 때문이다.

실세계 데이터는 현실에서 온다. 기존 시스템, 경쟁사의 시스템 혹은 어떤 종류의 프로토타입 등에서 자료를 수집한다. 이는 전형적인 사용자 자료이다.
합성 데이터는 어떤 통계적 조건하에서 인공적으로 생성된다.


테스트를 테스트하기
 

완벽한 소프트웨어를 작성 할 수 없기 때문에, 완벽한 소프트웨어 역시 작성 할 수 없다. 그렇다면 테스트를 테스트할 필요가 있다.
어떤 버그를 감지해 내는 테스트를 작성한 후에, 그 버그가 의도적으로 생기도록 한 다음 테스트가 불평을 해대는지 확인하라. 이렇게 하면 실제로 버그가 생겼을 때 테스트가 그걸 잡아 낼 것이라고 확신 할 수 있다.

<실용주의 프로그래머 팁>
파괴자를 써서 테스트를 테스트하라.

정말 테스트에 대해 심각하게 생각한다면, 프로젝트 파괴자를 임명 할 수 있다. 파괴자의 역할은 소스 트리의 카피를 별도로 만들어 취한 다음, 고의로 버그를 심고 테스트가 잡아 낼지 검증하는 것이다.


철저한 테스트
 

테스트가 올바르다는 확신이 들고,  여러분이 만든 버그도 찾아낸다면, 코드베이스를 충분히 철저하게 테스트했다는 것을 어떻게 알 수 있을까?
 
한마디로 답하자면 '알 수 없다'. 그리고 앞으로도 알 수 없을 것이다. 하지만 시장에는 여기에 도움되는 상품들이 있다. 커버리지 분석 도구는 테스트 중에 코드를 지켜보고, 코드의 어느 라인이 실행되지 않았는지 기억한다. 이런 도구들 덕에 여러분의 테스트가 얼마나 포괄적인지에 대한 대체적인 느낌을 가질 수 있다. 하지만 100% 커버리지를 기대하지는 마라.

우연히 코드의 모든 라인이 실행 될지라도, 그게 전부가 아니다. 정말로 중요한 것은 프로그램이 갖는 상태의 개수다. 상태는 코드 라인들과 동등하지 않다.
예컨데, 0에서 999사이의 정수 두 개를 받는 함수를 가정 해 보자.

int test(int a, int b) {
    return a / (a + b)
}

이 세 줄짜리 함수는 이론상으로 1,000,000가지의 논리적 상태를 갖는다. 그 가운데 999,999개는 제대로 작동 할 것이고, 하나는 그렇지 못할 것이다(a, b가 모두 0일 때). 코드의 이 줄을 실행했다는 것을 아는 것만으로는 이런 사실이 드러나지 않는다. 프로그램의 모든 가능한 상태를 분별해야 할 것이다. 불행히도, 일반적으로 이것은 정말로 어려운 문제다.

<실용주의 프로그래머 팁>
코드 커버리지보다 상태 커버리지를 테스트하라.

심지어 훌륭한 코드 커버리지가 있어도 테스트를 위해 사용하는 데이터는 여전히 상당한 영향을 미칠 뿐 아니라, 이보다 더 중요하게 여러분이 코드를 실행하는 순서가 가장 큰 영향을 미칠 수 있다.


언제 테스트할까

많은 프로젝트에서 사람들은 테스트를 마지막 일 분까지 미룬다. 데드라인의 날카로운 모서리에 닿는 순간까지. 그것보다는 훨씬 일찍 시작해야 한다. 실제 제품에 들어갈 코드는 나오자마자 테스트해야 한다.

테스트는 대부분 자동화 되어야 한다. 여기에서 중요한 것은, 우리의 '자동화'는 테스트 결과 해석의 자동화를 포함한다는 점이다.


그물 조이기

마지막으로 테스트에서 가장 중요한 개념을 밝히고자 한다. 뻔한 것이고, 거의 모든 교과서에서 이렇게 하라고 말하고 있다. 하지만 무슨 이유에서인지 대다수 프로젝트에서 지켜지지 않는다.

현존하는 테스트의 그물을 빠져 나가는 버그가 있으면, 다음번에는 그걸 잡아 낼 수 있도록 새 테스트를 추가해야 한다.

<실용주의 프로그래머 팁>
버그는 한 번만 잡아라.

 인간 테스터가 버그를 찾아내면, 그 때가 인간 테스터가 그 버그를 찾는 마지막 순간이 되어야 한다. 그 순간 이후부터는 무조건, 매번, 예외 없이, 아무리 사소한 것일지라도, 개발자가 "그건 앞으로 절대 다시 일어나지 않을 겁니다."라고 불평을 하더라도 해당 버그를 확인 할 수 있게 자동화 테스트들을 수정해야 한다.

왜냐면 그런 일은 앞으로 다시 일어날 것이기 떄문이다. 게다가 우린 자동화 테스트가 우리를 대신해 찾아 줄 버그까지 추격할 시간이 없다. 우리는 새 코드를(그리고 새 버그도) 작성하는 데 시간을 보내야 한다.


실용주의프로그래머
카테고리 컴퓨터/IT > 프로그래밍/언어
지은이 앤드류 헌트 (인사이트, 2007년)
상세보기
 
  

다익스트라의 테스팅 관련 명언

Posted by epicdev Archive : 2011. 10. 6. 19:34
다익스트라는 "테스팅은 버그의 존재만 보여 줄 수 있지 버그의 부재까지는 보여 줄 수 없다"는 명언을 했다.
그렇다면 그가 생각하는 버그를 없게 만드는 방법은? 수학적 증명과 단순화.

토니 호아의 다음 명언이 힌트가 될 것 같다. "소프트웨어 설계를 구축하는 두 가지 방법이 있다. 하나는 아주 단순하게 만들어서 명백히 결함이 없도록 하는 것이고, 다른 하나는 아주 복잡하게 만들어서 명백한 결함이 없도록 하는 것이다."

 

실용주의프로그래머
카테고리 컴퓨터/IT > 프로그래밍/언어
지은이 앤드류 헌트 (인사이트, 2007년)
상세보기


'Archive' 카테고리의 다른 글

Exception에 관하여  (0) 2011.10.08
가차 없는 테스트  (0) 2011.10.06
소프트웨어를 테스트하라  (0) 2011.10.05
테스트하기 쉬운 코드  (0) 2011.10.05
/etc/passwd 파일의 포맷  (0) 2011.10.04
  
 «이전 1 ··· 7 8 9 10 11 12 13 ··· 17  다음»