메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

Enterprise Flex RIA 해부(11) : 테스트하기

한빛미디어

|

2008-10-06

|

by HANBIT

10,974

제공 : 한빛 네트워크
저자 : Tony Hillerson
역자 : 이준하
원문 : Anatomy of an Enterprise Flex RIA Part 11: Testing What We Have

테스트 프레임워크의 개요에 대해서는 이전에 소개하였다. 이제 작업하기를 원하는 데이터베이스에서 만들어진 엔티티를 처리하는 것을 살펴보기 위한 몇 가지 유닛 테스트를 만들어보자.

TestNG

Maven은 JUnit 와 TestNG 2개의 자바 테스트 프레임워크를 바로 사용할 수 있도록 구성되어있다. TestNG 는 JUnit 만큼 많이 알려지지는 않았지만 몇몇 흥미로운 기능을 가진 매우 멋진 프레임워크이다. 그중 하나가 그룹 테스트이다. Maven에서 TestNG 를 사용하기 위해서는 TestNG 의 설정파일을 찾을 수 있도록 POM(Project Object Model) 에 설정만 해주면 된다.

그룹 테스트의 기능은 특정 시간대에 특정 테스트를 실행하거나 또는 원하지 않는 특정 그룹을 제외하고 실행하는 두 가지 형태가 있다.

아래는 TestNG 를 위한 설정 파일이다:

    
         
              
                   
                   
              
         
         
              
         
    

    
         
              
                   
              
         
         
              
         
    

우리는 패키지나 이름을 통해서 그룹 내에 테스트를 포함할 수 있다. 이름은 실제 클래스의 이름이 주어진다. 그리고 이러한 형식은 다음 내용에서 살펴볼 수 있다. 아래에서는 Maven 이 테스트할 클래스를 어디서 찾을 수 있는지 알려주는 데이터 프로젝트를 위한 POM 설정파일 중 관련 부분만 살펴보도록 하겠다.
...

    
         maven-surefire-plugin
         
              
                   
                        ${basedir}/src/test/resources/testng.xml
                   
              
              true
              false
              
                   
                        java.class.path
                        target/classes
                   
              
        
    
...
Maven 의 surefire 플러그인은 단위 테스트를 관리해준다. 우리는 TestNG 설정파일을 어디서 찾을 수 있는지 설정해주었다. 테스트가 병렬로 실행(빠르게 실행하기 위하여) 되기 위해서는 에러가 발생할 경우 생길 수 있는 스택트레이스(stack trace)와 같은 중요하지 않은 기능들은 동작하지 않도록 한다. 그리고 클래스패스에 target/classes 내에 있는 모든 클래스가 등록되도록 한다.

DBUnit

DBUnit 는 각각의 테스트를 실행하기 전에 데이터 세트를 데이터베이스에 심을 수 있도록 해주는 프레임워크이다. 여기에서는 다음 경로에 있는 예제를 사용한다. bookie-data/src/test/resources/fds/examples/bookie/datasets/searchable_books_dataset.xml:


    
    
    
    

    
    
    
    
    

    
    
    
    
    
    
    

dataset 루트태그 내에 각각의 태그들은 태그의 이름으로 구분되는 테이블에서의 각각의 데이터 열과 일치하게 된다. 여기에서는 4개의 subjects, 5명의 people, 7권의 books 데이터를 만드는 것을 볼 수 있다. 그리고 books 테이블은 subjects 와 authors 테이블과의 관계가 설정되어있는 것을 알 수 있다. people 테이블 데이터중 하나는 card_number 속성 값을 가진 user 이다. 이것은 몇 가지 중요한 것들을 테스트하기 위하여 알맞게 적당한 데이터 세트이다. books 테이블에서 subject 나 author 로 데이터를 찾으려고 할 때 또는 authors 나 subjects 테이블을 찾을 때 또는 user 가 로그인하려고 할 때 우리는 어떻게 이러한 데이터를 데이터베이스로부터 획득할 수 있는지 알아볼 것이다. (참고 : 이전 part 9 "명사와 엔티티 빈즈" 에서 user 와 authors 에 대하여 people 로 통칭하는 것으로 하였다.)

The Tests

테스트를 두 가지 형태로 나누어보기로 하자: 간단한 생성, 갱신, 삭제(앞글자만 따서 CRUD 라고 한다) 하는 작업과 user 의 이력관리를 지원하는 persistence layer 부분을 테스트하는 작업이다. 먼저 기본 테스트 클래스를 확장하여 BaseTest 클래스를 만들도록 하겠다:
package lcds.examples.bookie;
...
@Test(groups="unit")
public class BaseTest extends AssertJUnit {
 
}
이것은 우리가 만들려고 하는 모든 테스트 클래스를 "unit" 이라는 기본 테스트(우리는 이것을 다른 테스트로 override 할 수 있다)그룹에 속하도록 하며 AssertJUnit 클래스를 확장한 매우 간단한 클래스이다. TestNG 는 전형적인 Java 1.4 의 검증(assertions) 기능을 사용하기 때문에 여러분이 JUnit 에서 사용했던 것처럼 검증메서드를 사용할 수 있게 하여 준다. 다음은 몇 가지 기능을 더하여 BaseEJBTest 클래스를 만들 것이다.
package lcds.examples.bookie;
...
@Test(groups="functional")
public abstract class BaseEJBTest extends BaseTest {
 
     private static EJB3StandaloneDeployer deployer;
     private static ConnectionHelper connectionHelper;
     private static DatabaseConnection connection;
 
     @BeforeSuite
     public static void startEmbeddedJBoss() throws Exception {
           EJB3StandaloneBootstrap.ignoredJars.add("rt.jar");
           EJB3StandaloneBootstrap.boot(null);
  
           deployer = EJB3StandaloneBootstrap.createDeployer();
           deployer.getArchivesByResource().add("META-INF/persistence.xml");
           deployer.create();
           deployer.start();
  
           // Configure the DAOLookupHelper
           DAOLookupHelper.getInstance().setJNDIPrefix("");
      }
     
     @BeforeSuite
     public static void setUpConnection() throws Exception {
           connectionHelper = new ConnectionHelper();
      }
먼저 우리는 테스트를 "functional" 그룹에 속하도록 할 것이다. 이것은 BaseTest 클래스에서의 unit 그룹을 재정의한 것이다. 그렇기 때문에 모든 EJB 테스트는 functional 그룹에 속하게 된다. 우리는 실제로는 어떤 단위 테스트도 가지고 있지 않는다. 단지 그곳에 가져다놓는 것만으로 여러분은 그룹테스트가 어떻게 인식되는지 볼 수 있다. 그 다음에 TestNG 가 startEmbeddedJBoss 를 이번 테스트 단위에서 어떤 테스트보다 먼저 동작하도록 알려주기 위해서 @BeforeSuite 어노테이션(annotation)을 사용할 것이다. Embedded JBoss 는 우리가 JBoss 가 제공하는 중요한 서비스에 접근하기 위하여 JBoss 컨테이너 안에서 실행할 필요가 없도록 하는 기능을 가지고 있다. 이것은 정말 시간을 절약하게 해준다. 기본적으로 startEmbeddedJBoss 는 몇몇 기본 설정작업을 해주고 디플로이어를 생성하여 준다. 디플로이어는 META-INF/persistence.xml 에 정의된 어떤 저장소를 찾아서 우리의 persistence unit 을 배포할 것이다. 그리고 디플로이어가 작업을 수행한다. DAOLookupHelper 는 embedded JBoss 와 실제 JBoss 에서 생기는 JNDI 와 관련된 간단한 문제를 해결하기 위하여 만든 단순한 클래스이다. Embedded JBoss 는 우리가 필요한 세션빈이 어디에 있는지 찾을 수 있는 하나의 JNDI context를 제공한다. 하지만 JBoss 서버 내에 여러분의 EAR 파일의 이름이 JNDI context의 앞부분에 첨부되어있기 때문에 loopup helper 는 단지 그것만을 탐색하게 된다.(물론 EAR 파일의 이름은 원할 경우 변경할 수 있다.) 다음으로 @BeforeSuite 메서드는 테스트에서 DBUnit 를 위한 데이터베이스 커넥션을 가져오는 것을 관리해주는 간단한 helper 클래스로 사용되어지는 setUpConntction 이다. connectionHelper 를 생성할 때에 데이터베이스 커넥션 속성을 저장하기 위한 패키지 구조는 아래 test/resources 디렉터리에 있는 속성파일을 사용한다.
@AfterSuite
    public static void stopEmbeddedJBoss() {
          try {
                if (deployer != null) {
                      deployer.stop();
                      deployer.destroy();
                 }
                EJB3StandaloneBootstrap.shutdown();
           } catch (Exception e) {
                System.out.println("Error shutting down embedded JBoss after tests: " 
                                   + e.toString());
           }
     }

    @AfterSuite
    protected void tearDownDatabase() throws Exception {
          if (connection != null) {
                connection.close();
           }
     }
기대했던 것처럼 @AfterSuite 어노테이션 메서드는 suite 가 끝난 이후에 동작하게 된다. 그래서 모든 테스트가 종료된 이후에 정리하는 작업을 하게 된다.

먼저 우리는 stopEmbeddedJBoss 메서드에서 내장된 embedded JBoss 인스턴스를 종료시켜야 한다. 그리고 tearDownDatabase 메서드에서는 데이터베이스 연결을 끊을 것이다:
protected static DatabaseConnection getDatabaseConnection()
                                               throws Exception {
          if (connection == null) {
                connection = new DatabaseConnection(connectionHelper.getConnection());
           }
          return connection;
     }

    protected Object lookup(String name) throws Exception {
          Hashtable props = 
               new Hashtable();
          props.put(
               "java.naming.factory.url.pkgs",
               "org.jboss.naming:org.jnp.interfaces");
          props.put(
               "java.naming.factory.initial",
               "org.jnp.interfaces.LocalOnlyContextFactory");
          InitialContext ic = new InitialContext(props);
          return ic.lookup(name);
     }
    
}
그리고나서 다음과 같은 몇 개의 helper 메서드가 동작한다: getDatabaseConnection 메서드는 connectionHelper 로부터 커넥션을 가져온다. 그리고 lookup 메서드에서는 embedded JBoss 를 위한 설정을 적절하게 함으로써 JNDI로부터 다른 것을 찾는 것을 도와준다. 여기에는 프로세스의 생성과 업데이트를 간단하게 테스트해볼 수 있는 몇 개의 샘플이 있다:
package lcds.examples.bookie.entity;
...
public class TestCrud extends BaseEJBTest {
 
     @Test
     public void testCRUDSubject() throws Exception {
           Subject s = new Subject();
           s.setName("Zymurgy");
  
           SubjectDAO dao = DAOLookupHelper.getInstance().getSubjectDAO();
           s = dao.merge(s);
           Integer id = s.getId();
           assertNotNull(dao.findById(id));
  
           s.setName("Beer Making");
           dao.merge(s);
           assertEquals("Expected Zymurgy to be updated to Beer Making", "Beer Making", 
                        dao.findById(id).getName());
  
           dao.remove(s);
           assertNull(dao.findById(id));
      }
이것 말고도 몇 개의 테스트가 더 있다. 이 테스트는 전형적이다. 이것을 통해서 우리가 어떻게 세션빈을 이용하여 엔티티를 생성하고 업데이트하고 삭제하는지를 보여준다. 좀 더 다른 테스트를 살펴보자. 여기에는 세션빈의 search-type 기능을 활용한 예제가 있다:
...
public class TestBrowsing extends BaseEJBTest {
     ...
     @BeforeClass
     public void setup() throws Exception {
           connection = getDatabaseConnection();
           URL dsPath = 
      this.getClass().getResource(
      "/fds/examples/bookie/datasets/searchable_books_dataset.xml");
           searchableBooksDataset =
                new FlatXmlDataSet(
                     new FileInputStream(dsPath.getFile()));
  
           DatabaseOperation.CLEAN_INSERT.execute(
                connection, searchableBooksDataset);
      }
 
     @Test
     public void testSignIn() throws Exception {
           PersonDAO dao =
                DAOLookupHelper.getInstance().getPersonDAO();
           int user_id =
      Integer.parseInt(
           (String)searchableBooksDataset.getTable("people").
                getValue(0, "id"));
           Person user = dao.findById(user_id);
           String cardNumber = 
           (String)searchableBooksDataset.getTable("people").
                getValue(0, "card_number");
           assertEquals(user.getCardNumber(), cardNumber);
           Person cardHolder = dao.findByCardNumber(cardNumber);
           assertEquals(user, cardHolder);
      }
 ...
새로운 어노테이션인 @BeforeClass 는 TestNG 가 클래스내의 모든 테스트 이전에 해당된 메서드를 실행할 것이라는 것을 알려주고 있다. setup 메서드는 getDataBaseConnection 기본 클래스를 통해서 커넥션을 가져오게 된다. 그리고나서 앞에서 이야기했던 데이터를 설정하기 위한 참조를 가져오게 된다. DBUnit 의 한부분인 FlatXmlDataSet 생성자 내에 해당 URL 을 전달하게 된다. 그리고나서 그 객체를 CLEAN_INSERT DatabaseOperation 으로 전달하게 된다. claean insert 과정은 먼저 data set 내에서 참조하고 있는 테이블을 비워내고 data set 으로부터 data 를 집어넣는다. 매번 테스트마다 setup 메서드가 실행되기 때문에 우리는 데이터베이스가 매번 테스트에서 초기화되어진 상태를 유지할 수 있다고 확신할 수 있다.

testSingIn 메서드는 data set 으로부터 data 를 사용하는 테스트의 예제이다. 먼저 data set 의 첫 번째 열에 있는 ID 를 찾는다. 그리고 나서 해당 ID 를 가지는 person 을 찾고 data set 으로부터 해당 person 의 card number 를 가져온다. 이렇게 가져온 card number 가 찾아낸 person 의 card number 와 동일하다는 것을 보증한다. 그리고 card number 에 의하여 person 을 찾으려고 할 때 person 을 찾는 것을 쉽게 만들어준다.

[그림 14]에서는 테스트 내에서 어디서 클래스와 리소스를 찾을 수 있지는 보여준다. src/test/resources 폴더 내에는 embedded JBoss 설정에 필요한 많은 파일들이 있다. 당신이 원한다면 살펴보아도 괜찮지만 단지 그것들이 무엇을 하는지만을 보여줄 뿐이다.


[그림 14] 지금까지 사용된 클래스와 리소스들

이번 테스트들은 아주 단순한 것들이었다. 하지만 테스트되지 않는 코드는 무슨 일을 벌일지 모른다는 것을 명심해야 한다. 이것은 마치 시험을 대비하는 수험생들이 매일 밤 공부하는 것과 같다. 그리고 시험이 닥쳤을 때에도 그들은 안심할 수 있다. 그는 "나는 결코 헛된 고생을 한건 아니야. 시험이 너무 쉬웠어요" 라고 이야기할 수 있다. 한 뭉치의 간단한 테스트도 역시 시간낭비는 아니다. 그것으로 인해 안정성을 확보할 수 있는 것이다. 코드의 변경은 복잡한 코드뿐만 아니라 간단한 부분이라도 깨질 수 있다.

잔소리는 이쯤하고 이제 서비스 레이어 부분을 작성하는 것을 살펴봄으로써 이러한 엔티티들이 자바에서 플렉스로 좀 더 다가갈 수 있도록 할 수 있다.

다음 장에서 우리는 서비스 레이어를 위한 Maven 빌드와 LCDS(Live Cycle Data Service) 어셈블러의 개념에 대한 소개를 할 것이다. 여기를 클릭하면 전체 시리즈(영문)를 볼 수 있다.
TAG :
댓글 입력
자료실

최근 본 상품0