Eclipse

 ec.bmpju.bmp

 

목차


  1. 1. 다운로드와 설치
  2. 2. Eclipse 맛보기 및 기본 개념
    1. 간단한 Java 프로그램 작성 - HelloWorld 예제
  3. 3. JUnit을 이용한 테스트
    1. JUnit이란
    2. JUnit의 필요성
    3. JUnit을 위한 테스트코드 작성
    4. JUnit과 이클립스

 

1. 다운로드와 설치#

Eclipse SDK 설치

Eclipse 최신 버전은 Eclipse project download(http://www.eclipse.org/downloads/index.php)에서 다운로드할 수 있다. 2009년 5월 26일 현재 Eclipse 버전은 3.4 이다.

 

Java 런타임 설치

Eclipse를 실행하려면, 버전 1.4.1 이상의 Java 런타임 즉, J2SE 1.4.1 이상의 JRE나 JDK가 설치되어 있어야 한다. Eclipse SDK에는 자바 런타임이 포함되어 있지 않으므로 Java런타임이 없다면 이를 먼저 설치해야 한다.

 

 

2. Eclipse 맛보기 및 기본 개념#

간단한 Java 프로그램 작성 - HelloWorld 예제#

ec1.bmp 

위의 그림에서 오른쪽에 박스로 표시된 부분은 퍼스펙티브 바라 한다. 툴바 아래쪽에는 네 개의 분리된 영역이 있는데, 이 분리된 각각의 영역을 뷰라 하고, 네 개의 뷰를 포함하는 전체 영역을 퍼스펙티브라 한다. 위 그림은 Java 프로그램을 개발할 떄 사용하는 Java 퍼스펙티브를 나타낸다. Eclipse에서는 윈도우 전체를 워크벤치라 부른다.

프로그램을 작성하기 위해 가장 먼저 해야 할 일은 프로젝트를 생성하는 것이다. 메뉴바의 File>New>Project를 선택하면 프로젝트 위저드가 뜬다.

 ec2.bmp

위저드에 있는 트리에서 Java Project 항목을 선택하고 Next 버튼을 누른 다음, 프로젝트 이름을 입력하는 난에 'Hello'를 입력한다.

 ec3.bmp

Contents에 있는 Directory 항목은 지금 생성하는 프로젝트에서 만드는 리소스를 저장할 위치를 나타낸다. JDK 호환성을 나타내는 항목과 프로젝트 레이아웃을 설정하는 항목도 있고 Next 버튼을 누르면 프로젝트에 대한 설정을 좀 더 할 수 있지만, 지금은 바로 Finish 버튼을 누르자. 위저드가 닫히면서 Hello 프로젝트가 생성되고, 밑의 그림과 같이 Package Explorer에 새로 생성된 프로젝트가 표시된다.

ec4.bmp 

이제 HelloWorld 클래스를 만들어 보자. 메뉴바에서 File>New>Class 을 눌러 위저드를 띄운다.

ec5.bmp 

클래스 위저드에서는 클래스의 이름, 위치, 패키지, 접근 지정자, 수퍼클래스 구현할 인터페이스 등을 지정할 수 있다. 클래스 이름으로 'HelloWorld'를 입력하고, 아래쪽의 체크박스 중에서 public static void main(String[] args) 체크박스를 선택한 후 Finish 버튼을 누르면, Hello 프로젝트의 디폴트 패키지에 HelloWorld.java가 만들어지고, 에디터에는 HelloWorld 클래스 코드가 생성된다.

 resize_ec6.bmp

main() 메서드 안에 콘솔로 'Hello, world' 를 출력하도록 다음 코드를 입력해보자.

  1. public static void main(String[] args)  {
  2. System.out.println("Hello world!");
  3. }

이렇게 수정한 후 저장하면(메뉴바에서 File>Save), 자동으로 컴파일된다. 컴파일이 문제없이 끝났으면, 실행해 보자. 메뉴바에서 Run>Run As>Java Application을 선택하면 실행된다. HelloWorld를 실행하면 워크벤치 아래쪽에 Console 뷰가 생기면서 'Hello world!'가 출력된다.

ec7.bmp 

 

 

 

3. JUnit을 이용한 테스트#

 

JUnit이란#

프로그램 테스트는 코딩과 함께 반드시 수반되는 작업이다. 테스트를 거치지 않고서 완제품을 출시할 수 없으며 프로젝트를 문제없이 진행했다고 할 수 없다.

JUnit은 단위 테스트를 좀더 수면 위로 끌어올리고 정형화시켜준다. JUnit을 이용하면 단위 테스트 결과로 활용할 수 있음은 물론, 최적화된 코드를 유추해내는 기능도 하므로 성능 향상도 함께 기대해 볼 수 있을 것이다. 그리고 테스트한 결과물로 단순히 텍스트 문서를 남기는 것이 아닌 Test 클래스를 남김으로써 다음에 인계할 개발자에게 테스트 방법 및 클래스의 History를 넘겨줄 수 있다.

JUnit은 xUnit이라는 단위 테스트 프레임워크의 자바 구현물이며, 에릭 감마와 켄트 벡에 의해서 최초로 작성되었다. JUnit은 오픈소스로 IBM의 CPL 저작권을 따른다. 현재 xUnit은 거의 모든 프로그래밍 언어로 구현되어 있다.

 

JUnit의 필요성#

JUnit을 도입하지 않은 테스트 환경

작성한 클래스가 제대로 동작하는지 알아보기 위해서 일반적인 테스트 방법은 소스 밑단에 main()메소드를 추가하고 작성한 클래스의 인스턴스를 만들어 메소드를 실행해 보는 것이다. 보통 이런 테스트 방법은 하나의 클래스 파일 내에서 코딩하게 된다. 물론 이런 방법이 아주 틀렸다거나 효율적인 방법이 아니라는 뜻은 아니다. 프로젝트를 하는데 작성한 클래스가 한 두개 정도라면 이 방법이 빠르고 편리하다. 이렇게 많은 클래스를 작성하는데 클래스마다 main() 메소드를 추가하고, 'java <클래스 파일>'로 일일이 실행시켜 보는 것도 어디까지나 한계가 있고 체계적이지 못하다.

간단하게 예를 들어 보자. 아래는 두 날짜의 차이를 계산하는 DayCounter 클래스다.

  1. // DayCounter.java
  2. package helloproject.junit1;
  3. import java.util.Calendar;
  4.  
  5. public class DayCounter {
  6. public int MILLIS_PER_DAY = 1000*60*60*24;
  7. private Calendar day1 = null;
  8. private Calendar day2 = null;
  9.  
  10. public void setDay1(Calendar day)
  11. {
  12. this.day1 = day;
  13. }
  14. public Calendar getDay1()
  15. {
  16. return day1;
  17. }

    public void setDay2(Calendar day)

  18. {
  19. this.day2 = day;
  20. }
  21. public Calendar getDay2()
  22. {
  23. return day2;
  24. }
  25. public long getDays()
  26. {
  27. // 1970년 1월 1일부터 계산한 miliseconds 단위의 날짜를 long으로 계산
  28. long l_remain_date = day1.getTime().getTime() - day2.getTime().getTime();
  29. // l_remain_date를 day로 환산
  30. long remain_date = l_remain_date/MILLIS_PER_DAY;
  31. return remain_date;
  32. }
  33. }

 

이제 이 클래스를 테스트하기 위한 다음의 코드를 getDays() 다음에 추가해 보자.

  1. public static void main(String[] args) {
  2. DayCounter counter = new DayCounter();
  3. Calendar day = Calendar.getInstance();
  4. day.set(2003, 10, 5);
  5. counter.setDay1(day);
  6. day = Calendar.getInstance();
  7. day.set(2002, 6, 2);
  8. counter.setDay2(day);
  9. System.out.println("day1 - day2 = " + counter.getDays());
  10. }

 

 그리고 다음과 같이 컴파일하고 실행한다.

  1. D:\junit-ex>javac helloproject\junit1\DayCounter.java
  2. D:\junit-ex>java helloproject.juni1.DayCounter
  3. day1 - day2 = 491
  4.  
  5. D:\junit-ex>_

위에서 나타난 결과와 같이 2003년 10월 5일에서 2002년 6월 2일을 뺀 날 수는 491이다. main() 메소드에서 DayCounter의 인스턴스를 만들고 테스트 값을 세팅한 후, System.out으로 결과를 출력한다.

위의 방법은 우리가 아주 흔하게 사용하는 테스트 방법이다. 현재 예제의 경우 클래스 하나짜리 프로그램이어서 main() 메소드 하나에서 처리가 가능하지만, 만약 단위 테스트를 필요로 하는 클래스의 개수가 늘어나게 되면, 이 예제의 경우처럼 main() 메소드를 다시 만들어야 한다. 그리고 이때 main() 메소드는 테스트용으로 작성된 것이기 때문에 실제 릴리즈를 하고 납품하는 단계에서 main() 메소드는 삭제되거나 정리되어야 한다. 그러므로 test 메소드로써 단명하게 되며 별도로 기록을 보관할 수 없다. 따라서 체계적으로 테스트를 관리할 수 없다.

 

JUnit을 도입한 테스트 환경

http://www.junit.org에서 JUnit(junit4.6.zip : 2009.6.8) 최신 버전을 다운로드한다.

JUnit을 사용하는 데에는 일반적인 테스트의 예제처럼 main() 메소드가 필요하지 않으며 대신 테스트를 위한 별도의 클래스를 작성한다. 따라서 앞서 본 DayCounter.java 예제에서 테스트를 위해 사용했던 main()메소드를 지우고, 다음의 클래스를 새로 작성한다.

 

  1. // DayCounterTest1.java
  2. package helloproject.junit1;
  3. import java.util.Calendar;
  4. import junit.framework.TestCase;
  5.  
  6. public class DayCounterTest1 extends TestCase {
  7.  
  8. public void testGetDays() {
  9. DayCounter counter = new DayCounter();
  10. Calendar day = Calendar.getInstance();
  11. day.set(2003, 10, 5);
  12. counter.setDay1(day);
  13. day = Calendar.getInstance();
  14. day.set(2002, 6, 2);
  15. counter.setDay2(day);
  16. // 결과를 알고 491로 입력해 보자.
  17. assertTrue(counter.getDays() == 491);
  18. }
  19. }

 

 이제 JUnit으로 테스트를 시작해 보자. 다운로드한 junit.zip 파일을 D:\에다 압축을 풀면 D:\junit이라는 디렉토리가 생길 것이다. 다음과 같이 컴파일하고 JUnit으로 위의 클래스를 실행시켜 보자.

  1. D:\junit-ex>javac -=classpath .;D:\junit\junit.jar helloproject\junit1\DayCounterTest1.java
  2. D:\junit-ex>java -cp .;D:\junit\junit.jar junit.textui.TestRunner helloproject.junit1.DayCounterTest1
  3. .
  4. Time: 0.047
  5.  
  6. OK (1 test)
  7.  
  8. D:\junit-ex>_

'Time: 0.047'은 testGetDays()를 테스트하는 데 걸린 시간이다. 아무런 메시지가 없이 끝난 것은 Exception이나 테스트 실패가 없었다는 것을 의미한다.

 

JUnit의 장점은 테스트의 검증을 별도의 클래스에서 작성하고, 이 Test 클래스를 실제 소스와 함께 보관할 수 있어 테스트가 체계적으로 관리될 수 있다는 데 있다. 실제 프로젝트에서 사용하게 되면 주요 클래스마다 Test 클래스를 쌍으로 두게 될 것이고, 주요 클래스의 메소드나 기능을 test로 시작하는 test 메소드를 Test 클래스에 작성하게 될 것이다. 그리고 JUnit을 이용해 어느 부분의 테스트에서 시간이 얼마나 걸리고 또 테스트 실패는 없는지 Exception을 발생하지는 않는지 등을 한 번의 실행으로 종합해서 볼 수 있다. Ant의 <junit> 태스크까지 활용하면 테스트 결과 화면도 멋진 HTML로 받아볼 수 있다.

 

JUnit을 위한 테스트코드 작성#

JUnit을 구성하는 기본적인 클래스는 junit.framework 패키지 안에 있으며, Test 인터페이스를 구현하는 TestCase, TestSuite 클래스가 있다. 또한 junit.runner 패키지 안에는 테스트를 실행해 주는 BaseTestRunner 클래스가 있으며, 결과 출력에 따라 junit.textui, junit.awtui, junit.swingui 패키지 밑에는 BaseTestRunner를 상속받는 TestRunner 클래스가 존재한다.

 

TestCase 클래스

가장 간단하게 JUnit를 사용하는 방법은 TestCase 클래스를 상속받은 클래스를 작성하는 것이다. 이 클래스에는 test로 시작하는 메소드만 나열하면 된다.

위에 나온 DayCounterTest1.java 예제 소스를 보면 TestCase 클래스를 상속받았고, testGetDays()라는 메소드만을 구현하였다.

 

TestSuite 클래스

TestCase 클래스를 상속받은 클래스만을 사용하다 보면, 일부의 test 메소드는 간혹 실행하지 않고 싶거나 특정한 test 메소드만 실행하고 싶을 때가 생긴다. 또는 Test 클래스를 한데 묶어서 한꺼번에 실행하고 싶은 경우도 발생한다. 이때 사용하는 것이 바로 TestSuite 클래스다.

다음과 같이 FirstTestCase, SecondTestCase라는 두 개의 Test 클래스를 먼저 만든다.

  1. // FirstTestCase.java
  2. import junit.framework.*;
  3.  
  4. public class FirstTestCase extends TestCase
  5. {
  6. public void testOne()
  7. {
  8. System.out.println("[FirstTestCase] test one");
  9. }
  10. }
  11.  
  12. // SecondTestCase.java
  13. import junit.framework.*;
  14.  
  15. public class SecodeTestCase extends TestCase
  16. {
  17. public void testTwo()
  18. {
  19. System.out.println("[SecondTestCase] test two");
  20. }
  21. }

 

위의 두 클래스를 모두 실행하기 위한 TestSuite 클래스는 다음과 같이 작성한다. TestSuite를 상속받는 방법이 아닌 suite()라는 메소드에서 TestSuite 클래스를 만들어서 리턴하는 방식으로 작성한다.

  1. // AllTest.java
  2. import junit.framework.*;
  3.  
  4. public class AllTest
  5. {
  6. public static Test suite()
  7. {
  8. TestSuite test = new TestSuite("All Test");
  9. test.addTestSuite(FirstTestCase.class);              // 방법 1
  10. test.addTest(new TestSuite(SecondTestCase.class));   // 방법 2
  11.  
  12. return test;
  13. }
  14. }

AllTest를 실행하는 방법은 TestCase 클래스를 실행하는 방법과 동일하다. 위의 예제를 실행한 결과는 다음과 같다.

  1. .[FirstTestCase] test one
  2. .[SecondTestCase] test two
  3.  
  4. Time: 0.015
  5.  
  6. OK (2 tests)

 

TestRunner 클래스

TestRunner 클래스는 junit.runner.BaseTestRunner 클래스를 상속받은 클래스로써 JUnit의 실행 주체가 되는 클래스다. 사용자의 환경에 맞게 UI를 선택할 수 있도록 하기 위한 것이다. UI별로 Text UI는 junit.textui 패키지 밑에, Swing UI는 junit.swingui 패키지 밑에, AWT UI는 junit.awtui 패키지 밑에 TestRunner라는 동일한 이름으로 구성되어 있다. 만약 자신만의 JUnit UI를 만들고자 한다면, BaseTestRunner 클래스를 상속받아서 TestRunner 클래스를 새로 구현하면 된다.

앞절의 AllTest.java 예제를 AWT UI를 이용해서 실행하면 다음과 같다.

  1. java -cp .;D:\junit\junit.jar junit.awtui.TestRunner AllTest

 

예제를 Swing UI를 이용해서 실행하면 다음과 같다.

  1. java -cp .;D:\junit\junit.jar junit.swingui.TestRunner AllTest

 

이 TestRunner는 다음과 같이 원하는 곳에서 호출 가능하다. 앞에서 만든 AllTest 클래스에 다음의 main 메소드를 추가해 보자.

  1. public static void main(String[] args)
  2. {
  3. junit.swingui.TestRunner.run(AllTest.class);
  4. }

 

그리고 다음과 같이 실행하면, Swing UI에서 테스트 결과를 확인할 수 있다.

  1. java -p .;D:\junit\junit.jar AllTest

 

Assertions : 비교 확인, 조건 확인, Null 확인

Assertion의 사전적 의미는 주장, 단언이다. 실행 중간중간에 어떤 값이나 조건이 맞거나 혹은 틀리다고 '주장하거나 단언한다'는 뜻이다.

Assertion을 이용하면 test 메소드 중간중간에 어떤 조건이나 객체의 비교를 통해서 문제점을 끄집어 낼 수 있도록 도와준다.

사용 방법은 다음의 메소드를 test 메소드 안에서 사용하면 된다. 그리고 비교한 결과나 조건이 false면, AssertionFailure라는 Failure를 내고 해당 test 메소드는 종료한다.

 assertEquals(primitive expected, primitive actual) : 두 개의 기본형(primitive) 변수의 값이 같은지 검사한다.

 assertEquals(Object expected, Object actual) : 두 개의 객체 값이 같은지 검사한다(내부적으로 equals() 메소드를 사용한다).

 assertSame(Object expected, Object actual) : 두 개의 객체가 같은지 검사한다(내부적으로 두 객체의 메모리 주소가 같은지를 검사한다).

 assertNotSame(Object expected, Object actual) : 두 개의 객체가 다른지 검사한다(내부적으로 두 객체이 메모리 주소가 다른지를 검사한다).

 assertNull(Object object) : 객체가 Null 인지를 검사한다.

 assertNotNull(Object object) : 객체가 Null이 아닌지를 검사한다.

 assertTrue(boolean condition) : 조건문이 true 인지를 검사한다.

 assertFalse(boolean condition) : 조건문이 false 인지를 검사한다.

JDK 1.4부터 Assertion이란 개념이 아예 자바 언어에 포함되었다. JDK 1.4를 쓰고 있다면, JUnit을 쓰지 않고도 assertion을 수행할 수 있다.

 

Fixture : 초기 값 설정 및 해제

테스트를 하려면 초기 값의 설정이 필요하다. 그런데 JUnit은 test 메소드마다 모든 값을 초기화한다. 다음 예제를 실행시켜 보자.

  1. import junit.framework.*;
  2. public class InitialValueTest extends TestCase
  3. {
  4. private int x = 10;
  5. public InitialValueTest(String arg)
  6. {
  7. super(arg);
  8. }
  9. public void testOne()
  10. {
  11. for(int i = 0 ; i < 100 ; i++)
  12. {
  13. x++;
  14. }
  15. System.out.println("testOne's x=" + x);
  16. }
  17. public void testTwo()
  18. {
  19. for(int i = 0 ; i < 10 ; i++)
  20. {
  21. x++;
  22. }
  23. System.out.println("testTwo's x=" + x);
  24. }
  25. }

어떠한 결과가 나올까? testOne 메소드 실행 후의 x값은 110이 되고, 이어서 testTwo 메소드를 실행했기 때문에 testTwo의 메소드 실행 후의 x값은 120이 될 것처럼 보인다. 그러나 실행한 화면은 다음과 같다.

  1. .testOne's x=110
  2. .testTwo's x =20
  3.  
  4. Time: 0
  5.  
  6. OK (2 tests)

즉, test 메소드를 실행 시마다 Test 클래스의 인스턴스를 초기화한다는 의미가 된다. 다시 말하면 test 메소드간의 값을 공유하지 못한다.

 

클래스의 인스턴스가 생성된 후에 새로운 값을 초기화하고자 한다면, 다음과 같이 setUp() 메소드와 tearDown() 메소드를 재정의하면 가능하다. 다음의 메소드를 추개해 보자.

  1. // 초기 값을 설정할 필요가 있을 경우, 생성자 초기화 이후에 실행 설명한다.
  2. public void setUp()
  3. {
  4. x = 20;
  5. }
  6. // 필요 없는 값은 이곳에서 해제한다.
  7. public void tearDown()
  8. {
  9. x = 0;
  10. }

이제 실행해 보면 다음과 같이 test 메소드 실행 전에 setUp 메소드가 호출되어 x의 값이 다시 설정되었음을 확인할 수 있을 것이다.

  1. .testOne's x=120
  2. .testTwo's x=30
  3.  
  4. Time: 0
  5.  
  6. OK (2 tests)

setUp과 tearDown은 DBConnection 등과 같이 test 메소드에서 공통적으로 초기화하고 해제해야 하는 것이 있을 때 사용하면 효과적이다.

 

Exception : 예외처리

test 메소드를 실행 중에 뜻하지 않은 Exception이 발생하거나 혹은 새로 정의한 Exception 클래스에 대한 테스트를 한다고 하자. 이를 위해 다음과 같은 코드를 작성한다.

  1. import junit.framework.*;
  2. public class ExceptionTest extends TestCase
  3. {
  4. public void testMyException()
  5. {
  6. try
  7. {
  8. throw new Exception("my exception");
  9. }
  10. catch( Exception e )
  11. {
  12. // 아무 것도 처리하지 않는다.
  13. }
  14. }
  15. }

위 코드의 실행 결과는 다음과 같다.

  1. Time: 0.016
  2.  
  3. OK (1 test)

이렇게 해서 Exception을 메소드 내에서 처리를 끝내버리면 JUnit의 결과로 Exception을 전달하지 못한다. 즉, JUnit의 Error 또는 Failure가 되지 않는다. JUnit에게 결과를 넘기려면 fail() 메소드를 사용해서 다음과 같이 수정한다.

  1. import junit.framework.*;
  2. public class ExceptionTest extends TestCase
  3. {
  4. public void testMyException()
  5. {
  6. try
  7. {
  8. throw new Exception("my exception");
  9. }
  10. catch( Exception e )
  11. {
  12. fail("my exception");
  13. }
  14. }
  15. }

 

JUnit과 이클립스#

JUnit은 이클립스에 완벽하게 통합되어 있어서 소스 테스트 용도로 이클립스 내에서 사용하기가 용이하다. 이클립스를 사용하면 JUnit용 TestCase와 TestSuite를 새로 작성할 때 기본 템플릿을 제공하여 파일을 만든다. 또한 main 메소드를 TestCase나 TestSuite 내에 작성하지 않아도, 이클립스에서 제공하는 JUnit 실행 프로그램을 실행시켜서 이클립스용 JUnit 뷰(Viewer)를 통해 테스트의 결과 여부를 확인할 수 있다. 물론 main 메소드를 사용해서 마치 실행 창에서 java 명령어를 통해 실행시킨 결과처럼 볼 수 있기도 하다. JUnit 뷰는 JUnit의 Text UI 나, Swing UI, AWT UI와 동일한 기능을 제공한다.