JAVA/Effective Java2010. 1. 13. 09:59
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}

public 생성자 대신 static 팩토리 메서드를 제공하면 다음과 같은 장점이 있다.

1. static 팩토리 메서드는 생성자와 달리 자신의 이름을 가질 수 있다.
    생성자 매개변수가 반환객체를 제대로 나타내지 못하면 이름을 잘 지은 static 팩토리 메서드가 더사용하기 쉽고
    이 메서드를 호출하는 클라이언트 코드도 이해하기 쉽다.
2. 생성자와 달리 호출때마다 매번 새로운 객체를 생성할 필요가 없다.
   
이미 생성된 인스턴스를 다시 사용할 수 있으며, 불필요하게 인스턴스들이 생성되는 것ㅇ르 방지하기 위해 이미 
    생성된 인스턴스를 저장했다가 반복 사용할 수 있다. 이렇게 인스턴스를 제어하는 일을 하는 클래스를 인스턴스
    제어 (instance controlled) 클래스라고 한다. 인스턴스를 제어하면 싱글톤, 인스턴스 생성불가 클래스를 만들 수
    있다.
3. 자신의 인스턴스만 반환하는 생성자와 달리 반환하는 타입의 어떤 서브타입의 객체도 반환할 수 있다.
4. 매개변수화 타입(parameterized type)의 인스턴스를 생성하는 코드를 간결하게 해준다.
  
static 팩토리 메서드의 단점

1. 인스턴스 생성을 위해 static 팩토리 메서드만 갖는 경우, public이나 protected 생성자가 없는 경우
   서브클래스를
만들 수 없다.
2. 다른 static 메서드와 구별할 수 없다. 

Posted by B정상
JAVA/Beginning2009. 2. 16. 11:47

유닉스 서버나 윈도우 기반 서버든 GC를 수행하는 시점에서는 해당 WAS의 컨테이너에서
서비스가 처리되지 않는다. 즉 GC가 많이 발생하면 할수록 응답시간에 많은 영향을 끼친다는 것이다.
그래서 GC의 기본지식은 알고 있으면 도움이 된다.

GC란

Garbage Collection 이란 말 그대로 쓰레기를 정리하는 작업이다.
자바 프로그래밍에서 쓰레기란 객체이다. 하나의 객체는 메모리를 점유하고 필요하지 않으면
메모리를 해제해야 한다.

자바에선 개발자가 메모리를 처리하기위한 로직을 만들 필요가 없고, 절대 만들어서도 안된다.

GC의 원리

  • 메모리 할당
  • 사용중인 메모리 인식
  • 사용하지 않는 메모리 인식

사용하지 않는 메모리를 인식하는 작업을 수행하지 않으면 할당한 메모리 영역이 꽉차서 WAS에
행(Hang:서버가 요청을 처리 못하는 상태)이 걸리거나 더 많은 메모리를 할당하려는 현상이 발생할 것이다.

JVM의 메모리는 크게 클래스 영역, 자바스택, 힙, 네이티브 메소드 스택의 4개 영역으로 나뉘지만
힙영역만 고려한다.(GC는 힙영역에서만 발생한다.)

위의 그림에서도 Young 영역과 Old영역에만 집중하자.
Young 영역은 Eden 영역과 두개의 Survivor 영역으로 나뉘므로 우리가 고려해야 할 메모리 영역은
4개의 영역인 것이다.

메모리에 객체가 생성되면 Eden 영역에 객체가 지정된다.
Eden영역에 데이터가 어느정도 쌓이면 Survivor영역으로 옮겨지게 되는데 두개의 Survivor 영역중
우선순위가 있는 것은 아니다.

Eden영역의 객체와 어느한쪽에 할당된 Survivor 영역이 차면, GC가 되면서 Eden영역의 객체과
꽉찬 Survivor 영역의 객체를 비어있는 Survivor 영역으로 이동시킨다.
그러다가 더 큰 객체가 생성되거나 더이상 Young 영역에 공간이 남지 않으면 Old 영역으로 이동하게 된다.

GC의 종류

  • 마이너GC : Young 영역에 발생되는 GC
  • 메이저GC : Old영역이나 Perm영역에서 발생하는 GC

GC가 어떻게 상호 작용하느냐에 따라서 GC방식에 차이나 나며, 성능에도 영향을 준다.
GC가 발생하거나 객체가 각 영역에서 다른 영역으로 이동할때 애플리케이션의 병목이 발생하면서
성능에 영향을 주게 된다.

4가지 GC방식

  • Serial Collector (시리얼콜렉터)
  • Parallel Collector (병렬콜렉터)
  • Parallel Compacting Collector (병렬컴팩팅콜렉터)
  • Concurrent Make-Sweep(CMS) Collector (CMS컬렉터)

여기에 명시된 네가지 GC방식은 WAS나 자바 애플리케이션 수행시 옵션을 지정하여 선택할 수 있다.

시리얼 콜렉터

Young 영역과 Old 영역이 연속적으로 처리되며 하나의 CPU를 사용한다.
콜렉션이 수행될 때 애플리케이션 수행이 정지된다.

  1. 살아있는 객체들은 Eden영역에 있다.
  2. Eden영역이 꽉차면 To Survivor 영역으로 살아있는 객체들이 이동한다.
    Survivor영역에 들어가기 큰 객체는 Old 영역으로 이동한다. 그리고 From 영역의 살아있는
    객체는 To Survivor 영역으로 이동한다.
  3. To Survivor 영역이 꽉찼을 경우, Eden 영역이나 From 영역에 남아있는 객체는 Old영역으로 이동한다.

이후 Old영역이나 Perm 영역에 객채들은 Mark-Sweep-Compact 콜렉션 알고리즘을 따른다.
이 알고리즘은 간단하게 *안쓰는거 표시해서 삭제하고 한곳으로 모으는 알고리즘*이다.

  1. Old 영역으로 이동된 객체들 중 살아 있는 객체를 식별한다. (Mark)
  2. Old 영역의 객체들을 훑어보는 작업을 수행하여 쓰레기 객체를 식별한다. (Sweep)
  3. 필요없는 객체들을 지우고 살아있는 객체들을 한곳으로 모은다. (Compact)

이렇게 작동되는 시리얼 콜렉터는 일반적으로 클라이언트 종류의 장비에서 많이 사용한다.
즉 대기 시간이 많아도 크게 문제되지 않는 시스템에서 사용된다.
자바 옵션 : -XX:+UseSerialGC

병렬콜렉터

이 방식의 목표는 다른 CPU 대기 상태로 남아 있는 것을 최소화 하는 것이다. 시리얼 콜렉터와 달리
Young 영역에서 콜렉션을 병렬 처리한다.
많은 CPU를 사용하기 때문에 GC부하를 줄이고 애플리케이션의 처리량을 증가 시킬 수 있다.

Old 영역의 GC는 시리얼 콜렉터와 마찬가지로 Mark-Sweep-Compact 콜렉션 알고리즘을 사용한다.
자바 옵션 : -XX:+UseParallelGC

병렬컴팩팅콜렉터

Young 영역의 GC 처리는 병렬과 동일하지만 Old 영역의 GC는 다음 단계를 거친다.

  • 표시단계 : 살아 있는 객체 식별
  • 종합단계 : 이전의 GC를 수행하여 컴팩션된 영역에 살아 있는 객체의 위치 조사
  • 컴팩션단계 : 컴팩션 수행단계, 수행이후엔 컴팩션된 영역과 비어있는 영역으로 나뉜다.

병렬콜렉트와 동일하게 여러 CPU를 사용하는 서버에 적합하다.
자바옵션: -X:+UseParallelOldGC

CMS콜렉터

로우 레이턴스 콜렉터(low-latency-collector)로도 알려져 있으며 힙메모리 영역이
클때 적합하다. Young 영역에 대한 GC는 병렬콜렉터와 동일하다.
Old 영역의 GC단계

  • 초기표시단계 : 짧은 대기시간으로 살아 있는 객체를 식별
  • 컨커런트 표시단계 : 서부 수행과 동시에 살아있는 객체를 표시
  • 재표시 : 컨커런트 표시단계에서 표시하는 동안 변경된 객체를 다시 표시
  • 컨커런트 스윕단계 : 표시되어 있는 쓰레기를 정리

CMS콜렉터 방식은 2개 이상의 프로세서를 사용하는 서버에 적당하다.가장 적당한 대상으로
웹서버가 있다.

강제로 GC시키기

System.gc() 메서드나 Runtime.getRuntime().gc()메서드를 쓰면 되지만 완전비추!!!!

왜냐하면 GC를 수행하는 동안 다른 애플리케이션의 성능에 영향을 미친다는 점에서

단위:ms(밀리초, 1/1000초)

구분 응답시간
System.gc() 메소드포함 750ms~850ms
System.gc() 메소드미포함 0.13ms~0.16ms

비교해보면 소스하나에 포함된 gc()메서드 만으로 5000배의 성능차이를 보인다.
실제 운영중인 시스템에 이코드가 있으면 응답속도에 미치는 영향은 실로 엄청나게 커진다.

Posted by B정상
JAVA/Unit Test2009. 2. 16. 11:38

JUnit4.X 이후 변화

비교대상 JUnit3.8이전 JUnit4.0이후
Jdk5.0 Pradigm 도입 - Annotation 사용
Assertion Method를 static import 사용
Test Class junit.framework.TestCase상속 TestCase 상속받지 않음
Set up, teat Down setUp(), tearDown() @Before, @After
(다수의 메서드 지정가능)
Test Method testXXX()로 정의 @Test
@Test(expected=Error.class)
Junit4.4이후 - assertThat(value,matcher statement);
assumeThat()추가

TestCase에 Main() 메서드를 추가하기.

테스트를 실행시킬때 여러가지 방법이 있습니다만 전체 클래스에 메인메서드를 추가해서 사용해도 편리하겠죠.
메인메서드는 org.junit.runner.JUnutCore#main()을 호출합니다.

package testJunit;

import static org.junit.Assert.*;

import org.junit.*;
import org.junit.runner.JUnitCore;

public class SampleTest01 {

    @Test
    public void getStringOne() {
    	String str ="JUnit4.5";
    	assertEquals(str.charAt(0),'b');
    }
    public static void main(String[] args){
	JUnitCore.main(SampleTest01.class.getName());
    }
}

실행결과


콘솔창에서 실행한 결과입니다.

assertEquals(str.charAt(0),'J');

올바른 내용이 나오도록 수정하고 확인하면


올바른 테스트를 축하 드립니다. 위의 내용을 Run As > Java Application 으로 실행한 결과 입니다.


setUp(), tearDown()에 대해 정정


우선 제대로 알지 못하고 짐작으로 퉁~ 친점 죄송합니다. 앞서 스터디상에서 설명드릴때 setUp()메서드와 tearDowm()메서드 대신
어노테이션 사용하는데 @BeforeClass @AfterClass를 쓴다고 말했습니다.
정정 : @Before 와 @After 입니다.

import junit.framework.*;
import java.util.*;

public class Tester extends TestCase {
      public Tester(String name) {super(name);}
      private List list = new ArrayList();
      public void testFirst() {
            list.add("one");
            assertEquals(1, list.size());
      }
      public void testSecond() {
            assertEquals(0, list.size());
      }
}

위에 간단한 소스를 적어 놓았습니다.
testFirst() 메서드에선 list에 "one" 을 추가하고 list의 사이즈가 1과 같은지 를 묻고 있습니다.
testSecond() 메서드에도 list의 사이즈를 0과 같은지 묻고있습니다.
결과는 직접실행해 보시기 바랍니다.(추측후 실행해보시면 더욱 재미있으실 겁니다.)

JUnit 관련 검색중 재미있는 글을 읽었습니다.
원문 :
http://martinfowler.com/bliki/JunitNewInstance.html

JUnit에서는 테스트메서드 호출시마다 인스턴스를 새로 만든다는 내용의 글입니다. JUnit에서는 분리를 중요하게 여겨서
테스트시 자신의 테스트가 정작 다른테스트의 오류 때문에 방해를 받아선 안된다. 그래서 분리의 원칙에 따라 테스트
메서드 호출마다 새로운 인스턴스를 만든다는게 글의 요지입니다.

이미 알고 계신 내용이라면 김이 빠지실지도 모르겠습니다만 처음 알게된 저로선 작은 테스트를 해보기로 하겠습니다.
초심으로 돌아가서 static 초기화와 인스턴스초기화, 생성자 main()메서드 등등의 호출순서
처음으로 Java라는 것을 접하고 나열한 위의 ~ 호출순서때문에 고민했던 기억을 되살려 보시기 바랍니다.
(고민안하셨다면 천재일지도...;;; )

package testJunit;

public class testClass01 {

	static{
		System.out.println("static 초기화 제일먼저 호출");
	}
	{System.out.println("인스턴스 초기화 호출");}

	public void testMethod01() {
		System.out.println("testMethod01 호출");
	}
	public void testMethod02() {
		System.out.println("testMethod02 호출");
	}
	public static void main(String[] args) {

		System.out.println("main 호출");
		System.out.println("인스턴스 생성전");
		testClass01 tc01 = new testClass01();
		System.out.println("인스턴스 생성후");
		tc01.testMethod01();
		tc01.testMethod02();

	}

}

결과값

static 초기화 제일먼저 호출
main 호출
인스턴스 생성전
인스턴스 초기화 호출
인스턴스 생성후
testMethod01 호출
testMethod02 호출


네 알고계신 내용을 복습차원에서 써 놓았습니다.
JUnit에서의 호출수순도 이러한지 살펴볼 시간입니다.

package testJunit;

import static org.junit.Assert.assertEquals;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.JUnitCore;

public class SampleTest02 {

    static {
        System.out.println("static 초기화 출력");
    }
    {
        System.out.println("인스턴스 초기화 출력");
    }
    public SampleTest02() {
        System.out.println("생성자 출력");
    }
    @BeforeClass
    public static void doBeforeClass() {
        System.out.println("@BeforeClass 출력");
    }
    @Before
    public void doBefore() {
        System.out.println("@Before 출력");
    }
    @Test
    public void testOne() {
        System.out.println("@Test1 출력");
    }
    @Test
    public void testTwo() {
        System.out.println("@Test2 출력");
    }
    @After
    public void doAfter() {
        System.out.println("@After 출력");
    }
    @AfterClass
    public static void doAfterClass() {
        System.out.println("@AfterClass 출력");
    }
    public static void main(String[] args) {
	System.out.println("main 호출");
	System.out.println("인스턴스 생성전");
        JUnitCore.main(SampleTest02.class.getName());
	System.out.println("인스턴스 생성후");
    }
}

결과값

static 초기화 출력
main 호출
인스턴스 생성전
JUnit version 4.5
@BeforeClass 출력
.인스턴스 초기화 출력
생성자 출력
@Before 출력
@Test1 출력
@After 출력
.인스턴스 초기화 출력
생성자 출력
@Before 출력
@Test2 출력
@After 출력
@AfterClass 출력

Time: 0

OK (2 tests)





  • SampleTest02 클래스가 로드되면서 static초기화 호출
  • main()메서드 호출
  • @BeforeClass메서드 호출
  • 이하의 내용이 테스트메서드 호출마다 반복됩니다.
    • 인스턴스 초기화 호출
    • @Before메서드 호출
    • @Test메서드 호출
    • @After메서드 호출
  • @AfterClass메서드 호출

@BeforeClass, @Before, @After, @AfterClass의 차이점도 아실 수 있었습니다.

assertThat의 사용법

JUnit4.4버젼에서 추가된 assertThat()은 다른 단정메서드보다 더 범용적으로 활용이 가능할 것 같습니다.

assertThat
public static <T> void assertThat(T actual, org.hamcrest.Matcher<T> matcher)
public static <T> void assertThat(java.lang.String reason, T actual, org.hamcrest.Matcher<T> matcher)

assertThat()메서드에 인수로 지정되어 있는 org.hamcrest.Matcher 클래스는 비교처리를 위해 추가된 클래스입니다.
비교 처리를 위해 import static 해두시면 편하실 겁니다.
(ex : AllOf, AnyOf, DescribedAs, Is, IsAnything, IsEqual, IsInstanceOf, IsNot, IsNull, IsSame)

assertThat의 사용예

assertThat(x, is(3));
assertThat(x, is(not(4)));
assertThat(responseString, either(containsString("color")).or(containsString("colour")));
assertThat(myList, hasItem("3"));
  • 기존의 단정메서드를 사용하는 것보다 assertThat을 사용할때 가독성이 높고 무엇을 비교하는지 쉽게 알 수있다.
  • 기존의 단정메서드를 사용해 실패했을때 보다 이해하기가 쉽다. (실패메세지의 변화)
  • 비교처리를 스스로 추가해 사용 할 수 있다.

assertTrue failure Message

assertTrue(responseString.contains("color") || responseString.contains("colour"));
// ==> failure message:
// java.lang.AssertionError:

assertThat failure Message

assertThat(responseString, anyOf(containsString("color"), containsString("colour")));
// ==> failure message:
// java.lang.AssertionError:
// Expected: (a string containing "color" or a string containing "colour")
//      got: "Please choose a font"

assumeThat의 사용법

JUnit4.4에서는, org.junit.Assume 클래스가 추가되었습니다. Assume는 테스트하기 전에
전제조건을 밝히기 위한 클래스입니다. 동작은 앞서 설명한 AssertThat()메서드와 거의 동등합니다.

assertThat()의 차이점은 assumeThat()메서드로 검증을 실패하더라도 JUInit프레임워크의 테스트가
성공한 것으로 처리합니다. Assume은 테스트를 하기위한 전제조건을 테스트하는 기능입니다.

package Junit4_X;

import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.hamcrest.CoreMatchers.*;
import org.junit.Test;

public class TestSample01 {

	@Test
	public void testMethod01() {
		assumeThat(0 ,is(0));
		assertThat(0, is(0));
		assertThat(0 ,is(1));
	}
}

만약 전제조건에서 에러가 발생하더라도, 후의 계속되는 테스트에는 무의미하다고 판단해 테스트 전체에 대해서
영향을 미치지 않게 하여 테스트를 성공하게 합니다. 다양한 환경에서 테스트할 필요가 있는 경우,
환경의존테스트 (OS종류에 따른 테스트등)를 만들고 싶을때가 있을것이라고 생각합니다.
그럴때 이용하시면 편리할 듯 합니다.

package Junit4_X;

import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.hamcrest.CoreMatchers.*;
import org.junit.Test;

public class TestSample01 {

	@Test
	public void testMethod01() {
		assumeThat(0 ,is(0));
		assertThat(0, is(0));
                assumeThat(0 ,is(1));
		assertThat(0 ,is(1));
	}
}


Posted by B정상
JAVA/Unit Test2009. 2. 16. 11:35

뜬금없는 API입니다. 이부분은 단위테스트 with JUint의 Chapter3에 관한 내용입니다.
3장의 내용은 단정메서드, 테스트 조합, 예외및 이름규칙으로 나뉘어 있습니다.
3장에서 다루고 있는 내용을 좀 의도하지 않은 내용으로 가는건지 모르겠습니다만
3.8 버젼과 4.5 버젼의 비교정리할까 하여 기본이 되는 API 화면을 캡쳐해 보았습니다.

단정 메서드

테스트 대상이 되는 메서드가 제대로 동작하는지 아닌지 판단하도록 돕는 헬퍼메서드가 있습니다.
보통 이런 메서드들을 통틀어 단정메서드라고 부릅니다.

Method
assertArrayEquas(String message ,java.lang.Object[] expecteds ,java.lang.Object[] actuals)
assertEquals(String message ,expected ,actual)
assertEquals(String message ,expected ,actual ,tolerance)
assertNull(String message ,Object obj)
assertNotNull(String message ,Object obj)
assertTrue(String message , boolean condition)
assertFalse(String message , boolean condition)
fail(String message

왠지 메서드의 명이 중요하다라는 생각이 들지 않으신지요? 메서드명만으로도 무슨일을 하는지 느낌이 오시죠.
assertEquals(String message ,expected ,actual) 을 보면 expected에는 기대하는 값,
actual은 테스트의 대상이 되는 값을 넣습니다. message는 테스트가 실패할 경우 출력될 문자열이며, 선택사항입니다.

assertEquals(String message ,expected ,actual ,tolerance)의 tolerance는 두 비교값이 얼마나
동일해야 하는지를 나타내는 오차한계입니다. 실수형의 데이터비교시 사용하게 됩니다.
ex:assertEquals(3.33, 10.0/3.0, 0.01)

fail은 테스트 실패시 메세지가 있으면 출력해 줍니다.

@Test
      public void verifyArrayContents() throws Exception{
         String[] actual = new String[] {"JUnit 3.8.x", "JUnit 4", "TestNG"};
         String[] var = new String[] {"JUnit 3.8.x", "JUnit 4.1", "TestNG 5.5"};
         assertArrayEquals("the two arrays should not be equal", actual, var);		
      }

새롭게 추가된 assertArrayEquals() 입니다. 배열의 값이 동일한지 비교처리 가능합니다.

테스트 조합

TestClassOne.java
package chap3;

import junit.framework.TestCase;

public class TestClassOne extends TestCase {

	public TestClassOne(String method) {
		super(method);
	}
	
	public void testAddtion() {
		assertEquals("testAddtion : true",4, 2+2);
	}
	
	public void testSubtraction(){
		assertEquals("testSubtraction : true",0, 2-2);
	}
}
TestClassTwo.java
package chap3;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class TestClassTwo extends TestCase {

	public TestClassTwo(String method) {
		super(method);
	}
	public void testLongRunner() {
		TSP tsp = new TSP();
		assertEquals(2300, tsp.shortestPath(50));
	}
	public void testShortTest() {
		TSP tsp = new TSP();
		assertEquals(140, tsp.shortestPath(5));
	}
	public void testAnotherShortTest() {
		TSP tsp = new TSP();
		assertEquals(586, tsp.shortestPath(10));
	}
	
	public static Test suite() {
		TestSuite suite = new TestSuite();
		suite.addTest(new TestClassTwo("testShortTest"));
		suite.addTest(new TestClassTwo("testAnotherShortTest"));
		
		return suite;
	}
}
TestClassComposit.java
package chap3;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class TestClassComposit extends TestCase {

	public TestClassComposit(String method) {
		super(method);
	}
	
	public static Test suite() {
		TestSuite suite = new TestSuite();
		suite.addTestSuite(TestClassOne.class);
		suite.addTest(TestClassTwo.suite());
		return suite;
	}
	

}

모아모아 테스트입니다~.
구구절절 설명하고 있지만 역시 한장소에서 한번에 테스트하고 싶다는 말이더군요.

TestSuite테스트 조합단점 : 저는 TestSuite만으로 메서드들을 모아 비지니스프로세스 순으로 테스트가 가능할 것이다 라고
생각하고 있었습니다. 근데 메서드 3개정도를 모아놓고 실험을 했는데 절차적으로 테스트하는 것도
아니고 전혀 엉뚱한 방향으로 테스트가 흘러가더군요.
테스트 각각이 독립적이기 때문에 순서에 상관없이 테스트가 실행된다는 사실...
그러므로 테스트조합으로는 비지니스프로세스순의 테스트는 불가능하다 입니다.
앞으로 배우게 될 +모의객체+를 이용하여야 가능하다는 얘기를 듣고 충격을 먹었습니다.

액기스만 모아 볼까요...

public static Test suite() {
	TestSuite suite = new TestSuite();
	suite.addTestSuite(TestClassOne.class);
	suite.addTest(TestClassTwo.suite());
	return suite;
}
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({TestClassOne.class,
               TestClassTwo})
public class TestClassComposit {
    ...
}

JUnit의 @RunWith 어노테이션은 프레임워크에 내장된 러너가 아닌 다른 러너를 통해 특정 테스트 클래스를 손쉽게 실행할 수 있습니다.
@RunWith 어노테이션에서 지정해야 하는 Suite라는 이름의 스위트러너를 번들로 포함합니다.
또 테스트 스위트를 나타내기 위한 클래스 목록을 매개변수로 취하는 @SuiteClasses라는 주석을 제공합니다.

  1. setUp();
  2. testMethod();
  3. tearDown();

메서드는 각각 테스트 전후에 환경설정및 정리시에 호출하여 사용합니다.
굳이 예를 들자면, 데이터베이스 연결객체가 필요하다고 할때 각테스트할 메서드에 일일이 넣지않고 위의 메서드를 활용하면 좋겠죠.

protected void setUp(){
      dbConn= new Connection("Oracle","1521","ID","PASS");
      dbConn.connect();
}
protected void tearDown(){
      dbConn.disconnect();
}
public void testMethod() {
      ....
}

최신판에선 어떻게 사용하는지 살펴봐야죠~

import org.junit.AfterClass;
import org.junit.BeforeClass;


@Before
protected void setConn(){
      dbConn= new Connection("Oracle","1521","ID","PASS");
      dbConn.connect();
}

@After
protected void disConn(){
      dbConn.disconnect();
}

@Test
public void getMethod() {
      ....
}

느낌상 setUp(), tearDown() 내용도 사용방법도 거의 비슷합니다.
어노테이션이 갖는 장점은 이전에 JUnit가 갖는 일종의 규칙에 대한 지식이 없어도 이 메서드가
어떤 역활을 담당하는지 유추할 수 있다는 겁니다.

간단하게 JUnit에 대해서 살펴봤습니다.


Posted by B정상
JAVA/Unit Test2009. 2. 16. 11:33

머리로 아는 내용을 설명한다는 것이 정말 답답하더군요. 진도도 나가지 않고 답답한 맘뿐입니다.
차라리 단위테스트 설명을 올리는 것보다 Junit 테스트를 먼저해서 올렸으면 좀더 수월하게 했을지로 모른다는 생각이 드네요.
썰은 이쯤에서 마치고 테스트로 들어 가겠습니다.

  • 어떤 코드건 작성하기 전에 실패하는 자동화 테스트를 작성하라.
  • 중복을 제거하라

TDD의 들어가는 글에 나와있는 규칙입니다. 그리고 위의 두개가 TDD의 주제 이기도 합니다.
위에서 열거한 자동화된 테스트를 위한 툴이 저희가 공부할 Junit입니다.

JUnit은 현재 4.5버젼까지 나왔으며 단위테스트 with JUnit 에서 사용하는 버젼은 3.8x을 사용하고 있습니다.

  • 3.8x 버젼의 권장사항
    1. TestCase를 확장한다.
    2. 메서드명은 test로 시작해야 한다.
      만약 테스트 하려는 메서드가 createMethod()라면 testCreateMethod()로 명명할 것을 권장한다.

책에 부록으로 있는 Eclipse에서 Junit사용하기를 보시면 신호등관련 프로그램의 테스트가 나와 있습니다.

package TrafficLightModel;

import junit.framework.TestCase;


public class TrafficLightModelTest3_X extends TestCase {

      public void getTrafficLightModel() {

            TrafficLightModel a = new TrafficLightModel();
            assertTrue(a.getRed());
            //assertTrue(a.getYellow());
            //assertTrue(a.getGreen());

      }

}

얼핏 보아도 쉬운 내용임을 알 수 있습니다. getRed()메서드를 포함한 TrafficLightModel 클래스가 구현되어 있다면,
Junit를 실행하기 전까진컴파일에 있어서도 아무 문제를 일으키지 않습니다.


Failure_Trace 창의 에러메세지를 보시면 test를 찾을 수 없다고 나옵니다.
getTrafficLightModel() 메서드의 이름을 testTrafficLightModel() 로 바꿔 주시면 반가운 초록막대를 보실 수 있습니다.
즉, 권장사항이라고 얘기했지만 위의 두 규칙을 지키지 않으면 테스트 할 수 없는 것입니다.

Junit 3.8x 버젼때의 제약은 4.로 넘어가면서 자바5의 어노테이션과 만나 위의 권장사항은 따르실 필요는 없게 되었습니다.
물론 기존의 테스트소스를 Junit 최신판으로 다운받아 실행하는데 문제는 없습니다.
그저 좀더 간편해 졌기에 맛보기용으로 조금 올려 봅니다.

package TrafficLightModel;

import org.junit.Test;
import static org.junit.Assert.*;

public class TrafficLightModelTest4_X {

	@Test
	public void getTrafficLightModel() {
		TrafficLightModel a = new TrafficLightModel();
		assertTrue(a.getRed());
	}
}

클래스 계층구조는 더이상 필요하지 않고, 테스트로 작동할 메서드도 새롭게 정의된 @Test 어노테이션만 기술하시면 됩니다.

다시 책으로 돌아가 2장의 첫테스트와 만나 보겠습니다.
2장의 첫테스트는 정수배열을 받아들여 그중에서 큰값을 리턴해주는 프로그램입니다.

package first;

public class Largest {
	
	public int largest(int[] list){
		int max = Integer.MAX_VALUE;
		for (int index=0; index <list.length;index++) {
			if (list[index] > max)
				max = list[index];
		}
		return max;
	}
	
	public static void main (String args[]) {
	    Largest l = new Largest();
		System.out.println(l.largest(new int[] {7,8,-20,0,8}));
	}
}

저의 테스트 방식입니다. main()메서드를 사용해서 리턴값을 확인한다거나 println()문을 사용하여 제가 만들어 놓은 값을 확인합니다.
요즘도 값을 추출하거나 특정단어를 인식하는 간단한 메서드를 만들어 테스트할때 많이 사용합니다.

그리고 보통의 경우 테스트는 다음과 같은 방식으로 진행될 것입니다.

  1. 재빨리 테스트를 하나 추가한다.
  2. 모든 테스트를 실행하고 새로 추가한 것이 실패하는지 확인한다.
  3. 코드를 조금 바꾼다.
  4. 모든 테스트를 실행하고 전부 성공하는지 확인한다.

위의 말중에 재빨리와 실패라는 단어가 눈에 거슬릴지도 모르겠습니다만 잠시 무시해 주세요.

Lagest 클래스로 눈을 돌려 저희가 일련의 배열에서 큰값을 리턴해 주는 메서드를 작성하려 할때 메서드를 만들어
편한데로 확인하려 할것입니다. 위의 메서드에서도 원했던 값은 배열중 제일큰 8이란 값을 리턴해주길 바랬지만
끔찍하게도 2,147,483,647 이란 값을 리턴해 줍니다.

절대 실패할것이 아니고 단번에 제가 원한 답이 나와주길 바랍니다만 실패할때가 많습니다.

우연찮게 성공했다고 하여도 한번에 여러개의 값을 동시에 입력, 삭제할때 혹은 동일 값이 들어가 있거나 등의
앞장에서도 살짝쿵 설명했지만 경계조건의(대표값: null or 0)의 사이에서 의도치는 않았겠지만 실패요소들을
상당수 안고 있습니다.

위의 2번째 실패하는지 확인한다의 3번째 코드를 바꾼다 횟수가 많았던 만큼 현업 혹 윗상사의 눈밖에 날 위험은 현저히 줄어들겁니다.
2~3의 단계 무한반복후 의도한 값이 제대로 나오면 보통의 테스트는 여기서 끝나게 됩니다.
허나 하나 더 남아 있습니다.

5. 리팩토링을 통해 중복을 제거한다.

뭔가 앞에 말이 붙긴 했지만 어디서 봤던 말입니다. 중복을 제거 한다. 무슨 중복을 제거하는지는 차차 보시게 될듯합니다.
위에 써놓은 말은 토시하나 안틀리고 테스트 주도 개발에 나오는 내용입니다.
리팩토링을 통한 중복제거는 앞으로 소개하는 것으로 하고 넘어가겠습니다. (제 레벨이 아직;;; )

package first;

import junit.framework.TestCase;

public class LargestTest3_X extends TestCase {

	Largest l = new Largest();
	public void testGetLargest() {
		
		assertEquals(8,l.largest(new int[] {7,8,-20,0,8}));
	}
}

JUnit에서 다음같은 코드로 테스트를 하셨다면 Failure Trace창에서 이란 말을 보실 수 있습니다.

junit.framework.AssertionFaildError:expected:<8> but was:<2147483647>
at first.LargestTest3_X.testGetLargest(LargestTest3_X.java:10)

int max = Integer.MAX_VALUE;
를 사용한 부분을 max = 0으로 수정을 한 후 테스트를 하면 또다시 우리를 기쁘게 해주는 초록막대를
확인가능합니다.

그러나 문제가 있습니다. 배열의 정수가 모두 -값이라면 얘기가 달라집니다.
배열에는 (-41,-6,-30,-22) 값이 있을때 제가 원한 -8값을 돌려 주는 것이 아니라 0을 리턴하니
main()메서드로 얼핏 확인했다며 놓치고 지나칠 수 있었을 문제였습니다만

junit.framework.AssertionFaildError:expected:<8> but was:<0>
at first.LargestTest3_X.testGetLargest(LargestTest3_X.java:11)

네~ 실패했습니다. max = 0 이었던 값도 문제가 있었습니다.
max의 초기값을 음수보다 작게 만들어야 문제가 없음이 판명되었습니다. Integer.MIN_VALUE를 사용해야
다시 모두가 행복해지는 결과를 볼 수 있습니다.

공배열을 넣고 원하는 결과를 리턴 받을 수 있을까요??

package first;

import junit.framework.TestCase;

public class LargestTest3_X extends TestCase {

	Largest l = new Largest();
	public void testGetLargest() {
		assertEquals(8,l.largest(new int[] {7,8,-20,0,8}));
		assertEquals(-6,l.largest(new int[] {-41,-6,-30,-22}));
	}
	public void testGetEmpty() {
		l.largest(new int[] {});
		fail("테스트를 실패처리함.");
	}
}

네~ 실패했습니다. 책에 나와있는 설명으로 보면

' 단지 테스트를 생각해 보는것만으로 코드의 설계를 바꿔야한다는 것을 알아냈다는 사실에 주목하라.
이것은 전혀 특별한 일이 아니며, 실제로 우리가 테스트를 이용해 얻고자한 이득중의 하나이다.'

제가 플젝현장에서 메서드작성중 이런 결과를 얻어내고 메서드에 반영조치 했다면 충분히 특별하고
충분히 자랑질할 일이었을 듯 합니다만 책에선 특별한 일이 아니고 통상의 일이라 하니 그렇게 느낄때까지
사용할 수 밖에 없겠습니다.

Largest.java
int max = Integer.MIN_VALUE;
	if (list.length == 0) {
	    throw new RuntimeException("공배열입니다.");
         }

테스트에서 발생하는 * 예상된 예외에 대한 처리 *를 할때 fail을 사용한다고 합니다.

LargestTest3_X.java
public void testGetEmpty() {
		try{
			l.largest(new int[] {});
			fail("테스트를 실패처리함.");
		}catch(Exception e){
			assertTrue(true);
			
		}
	}

코드를 다음과 같이 수정후 다시한번 행복의 초록막대를 보실 수 있습니다.

package first;

import org.junit.Test;
import static org.junit.Assert.*;

public class LargestTest4_X {
	Largest l = new Largest();
	
	@Test
	public void GetLargest() {
		assertEquals(8,l.largest(new int[] {7,8,-20,0,8}));
		assertEquals(-6,l.largest(new int[] {-41,-6,-30,-22}));
	}
	@Test (expected=RuntimeException.class)
	public void GetEmpty() throws Exception{
			l.largest(new int[] {});
	}
}

위의 코드는 LargestTest3_X.java와 동일합니다.
try/catch 문을 사용하지 않고 expected 매개변수를 사용함으로 Exception 처리가 조금 달라졌습니다.

Posted by B정상
JAVA/Unit Test2009. 2. 16. 11:30

단위테스트란?

테스트 대상이 되는 코드 기능의 아주 작은 특정 영역을 실행해 보는, 개발자가 작성한 코드 조각이다.
대개 단위 테스트는 특정 상황에서 특정메서드를 시험해 본다.
어떤 코드 조각이 개발자가 생각하는 대로 동작하는지 증명하기 위해 수행하는 것이다.

기능테스트에 속한다. 개발자에 의한 개발자를 위한 테스트로 필수적이긴 하지만, 검증툴로서는 불충분하다.

테스트의 6가지 영역 - Right-BICEP


  • Right - 결과가 옳은가?
    예상한 결과가 옳은지 살펴보는 결과의 유효성 검사를 하는 것.
    이 코드가 옳게 동작한다면, 어떻게 그것을 알 수 있는가? 에 대해 요구사항이 완벽하지 않고
    대답이 명확하지 않다면 적어도 개발자 관점에서 몇가지는 생각해 낼 수 있다.
    물론 추측한 내용은 사용자 피드백에 의해 다듬어 조정해 가는 과정을 거쳐야 할 것이다.
    즉 어느시점에서든 생각한 대로 동작한다는 것을 증명할 수 있어야 한다.

  • B - 모든경계조건이 CORRECT 한가
    버그는 '경계조건' 근처 즉 코드가 평소의 루틴과 다르게 동작하는 조건에서 많이 발생한다.

    • Conformance - 형식일치
      이메일, 전화번호, 계좌번호, 파일이름등 형식화된 데이터부터 여러연결구조의 데이터등을
      테스트 하여 기대한 형식과 일치하는지 확인해야 한다.
    • Ordering - 순서
      고려해야할 또 하나의 영역은 큰데이터 모음에서 데이터의 순서나 데이터의 한부분의 위치이다.
      어떤 검색루틴이라 해도 반드시 대상이 시작부분이나 끝 부분에 있는 조건을 테스트 받아야 한다.
      많은 버그들을 이런 식으로 찾아낼 수 있다.
    • Range - 범위
      범위는 어떤 변수형이 필요하거나 원하는 값보다 범위를 허용하는 상황을 포괄적으로 함축하는 단어다.
      좋은 객체지향 설계에서는 나이나 방위처럼 한계가 있는 정수형 값(나이를 20,000살이라 입력하거나
      방위의 값을 365도로 입력하는 것 처럼)을 저장할때에는 그대로의 기본형을 사용하지는 않는다.
    • Reference - 참조
      메서드가 자기 영역을 벗어난 어떤 것들을 참조하는가? 외부 의존성이 있는가?
      클래스가 가져야 하는 상태는? 그 메서드가 제대로 동작하려면 그밖에 어떤 조건을 갖춰야 하는가?
      ex) 선언은 되었으나 정의되지 않은 전역변수
    • Existence - 존재성
      ex) null값을 갖는 List의 size() 메서드 호출(null,0,'')
      종료된 세션의 접근
    • Cardinality - 개체수
    • Time - 시간
      ex) 상대시간 (시간적순서)
      절대시간 (경과한 총 계산 시간)
      동시성 문제

  • I - 역관계확인
    논리적 역(Inverse)을 적용하여 검증해 볼 수 있다.

  • C - 다른 수단을 이용한 교차확인
    다른 수단을 이용해서 메서드의 결과를 교차 확인 할 수도 있다. 보통 어떤 값을 계산할 때에는 방법이 한 가지만 있는 건 아니다.
    어떤 한 알고리즘을 선택하는 이유는 그것이 더 좋은 성능을 내거나, 또는 그밖에 다른 좋은 특성이 있기 때문이다.
    제품에는 당연히 제일 좋은 것을 사용하겠지만, 테스트 시스템에서는 결과를 교차 확인하기 위해
    다른 알고리즘을 사용해 볼 수도 있다. 이 기법은 어떤 일을 수행하기 위한 알려진 방법이 있는데,
    제품 코드에 사용하기에는 지나치게 느리거나 유연성이 없는 경우에 특히 유용하다.

  • E - 에러 조건을 강제로 만들어 내기
    현실세계에서는 에러가 발생한다. 디스크가 꽉차고, 네트워크는 끊어지거나 프로그램은 갑자기 멈춘다.
    이런 에러를 일으켜 현실 세계의 문제들을 제대로 처리한다는 것을 테스트할 수 있어야 한다.

  • P - 성능특성
    성능은 그자체가 아니라 입력양이 많아지고, 문제가 복잡해지면서 성능이 변하는 경향을 말하는 것이다.
Posted by B정상
JAVA/Eclipse2009. 2. 12. 04:25
Posted by B정상
JAVA/JavaFX2009. 2. 8. 08:51
Posted by B정상
JAVA/Eclipse2009. 2. 6. 04:46
Ganymede(Eclipse 3.4 version)가 출시되고 UML2Tools 가 2008년 8월 새롭게 릴리즈 되었습니다.
확인해 보면 아시겠지만 심플해서 사용이 용이합니다.

 
오랜만에 들려 본 이클립스 사이트입니다. Modeling을 다운받으시면 바로 UML 사용이 가능하지만 그외 추가된 기능들로 인해 (300MB가 넘어갑니다.) 꽤 무겁습니다.
 
 
저희한테 필요한 기능은 MDT 패키지에 포함되어 있습니다.

 
MDT에서도 필요한 기능은 UML2 와 UML2 Tools 입니다.
zip파일을 다운받으셔서 eclipse > plugin 폴더에 추가해주셔도 됩니다

 
아니면 update manager를 사용하도록 하겠습니다.

 
add sites를 눌러 복사해온 주소를 넣어 줍니다.

 
이중에서 저희한테 필요한 uml2와 uml2tools 에 체크하여 install 버튼을 누르면
 
 
제대로 파일들을 가지고 있는지 저장소에서 검색후

 
로컬로 내려받게 됩니다.

 
다른 uml plugins를 사용하시려면 Plugin Directory에서 검색하셔서 위와 동일한 수순으로 install 하시면 됩니다.
Plugin Directory에서는 이클립스 버젼별로 사용하실 수 있는 (usecase, class digram 등등) 내역들과
사용자들이 점수를 매겨놓아서 참고할 수 있게 되어 있습니다.

 
아래는 이클립스에서 추가한 클래스 다이어 그램입니다.
Posted by B정상
JAVA/JavaFX2009. 2. 4. 01:09

Java FX란 스윙의 GUI 어플리케이션을 구축하기 위한 스크립트 환경입니다.
'Java FX Script' 라는 스크립트 언어는 보다 간단한 스크립트로 고도의  GUI 어플리케이션을
구축할 수 있게 해줍니다. 'Java FX Script'의 실행환경은 컴퓨터용의 Java FX DeskTop과 모바일용의
Java FX Mobile 로 계획되어 2009년 여름까지 릴리스 될 예정이죠.

하지만 왜 일부러 만들었을까요?
스윙이라면 Java를 사용하면 될텐데? 라는 의문을 갖는 사람이 많을 것입니다.
말 그대로 스윙은 Java를 사용하면 됩니다. 근데 왜 일부로 언어를 만들었을까요?
그것에 대한 답이 "RIA"입니다.

Rich Internet Application 의 뜻을 위키피디아에서 찾아 보면 전통적인 테스트탑 어플리케이션의
기능과 특징을 구현한 웹어플리케이션입니다. 주로 복잡한 조작을 할 수 없었던 웹브라우저 기반의 어플리케이션
을 대체 하기 위한 솔루션으로 사용되고 있습니다.
한국에서는 "X인터넷"솔루션이라고 불리지만 세계적으로RIA라는 표현을 많이 사용합니다.

즉 HTML 과 JavaScript 와 CSS로 만들어 졌던 웹페이지에 새로운 기능을 부가하여
동적인 어플리케이션을 작성할 수 있게 된것입니다.
하지만 한편으로는 웹 브라우저가 이러한 환경을 갖주치 못하면 조작불능사태가 발생하거나
이용에 제한을 받을 수 도 있다는 얘기 입니다.

즉 JavaScript로 된 프로그램은 웹브라우저 마다 호환성이 틀립니다.(물론 Ajax 역시
이러한 문제점을 안고 있기는 매한가지죠.) 
그렇다고 역동적인 웹어플리케이션을 이전의 글자만 가득한 정적인
웹 어플리케이션으로 돌아가는 것은 불가능한 것입니다.

그래서 RIA가 주목받는 것입니다.. 웹브라우저에 구속되지 않는 고도의 GUI를 갖는 웹어플리케이션.
예를 들면 어도브의 Flash가 있겠습니다.
하지만 사람들은 좀더 많은 것을 바라고 원하게 되지요.
웹브라우저에서 벗어나 어플리케이션으로 실행되는 웹프로그램도 말입니다.

이러한 흐름에 Adobe에서는 「AIR」, Microsoft에서는 「Silverlight」라는 RIA 환경이 차례차례로 발표됩니다
Sun사에서도 이에 대응하기 위해 즉 RIA 환경에 맞춰 Java FX를 등장 시킨 것입니다.

Posted by B정상