IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  자바  >

사람을 위한 자동화: 연속 테스팅 (한글)

코드 베이스가 변경될 때 마다 자동화 테스트 실행하기

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.


제안 및 의견
피드백

난이도 : 중급

Paul Duvall, CTO, Stelligent Incorporated

2007 년 4 월 17 일

개발자 테스팅을 향상시키고 싶으십니까? 사람을 위한 자동화 자동화 시리즈에서는, 개발 자동화 전문가 Paul Duvall이 소스 코드 변경에 따라 실행할 수 있는 다양한 유형의 자동화된 개발자 테스트에 대해 설명합니다. 애플리케이션 문제를 일찍 발견하는데 도움이 되는 Selenium, DbUnit, JUnitPerf 테스트 예제를 제공합니다.
시리즈 소개
개발자로서, 사용자를 위해 프로세스를 자동화 하고 있습니다. 하지만, 정작 우리들 자신의 개발 프로세스를 자동화 할 수 있는 기회를 간과하고 있습니다. 따라서, 사람을 위한 자동화 시리즈에서는 소프트웨어 개발 프로세스를 자동화 하는 방법을 연구하고, 자동화를 적용할 시기방법을 설명합니다.

Eclipse나 Ant 빌드 스크립트와 같은 IDE에서 단위 테스트를 실행하는 것은 애플리케이션의 품질을 보증할 수 있는 첫 걸음이다. 깨끗하게 구분된 빌드 머신에서, Subversion 같은 버전 컨트롤 저장소에서 소스 코드가 변경될 때 마다 단위 테스트를 실행하면 개발 사이클 동안 문제들을 확인하는데 도움이 될 수 있다. 더욱이, 컴포넌트, 함수, 성능 같은 다양한 유형의 개발자 테스트를 실행하면 개발 사이클에서 더욱 일찍 문제 부분을 발견할 수 있다.

Continuous Integration (CI) 환경에서 자주 실행되는 개발자 테스트는 코드 품질에 대한 스포트라이트와 같은 작용을 한다. 이러한 테스트들이 효과적으로 작성된다면 결함이 생성되자마자 바로 발견될 수 있기 때문이다. 자주 실행되지 않는 테스트는 효과가 떨어진다. 결함이 만들어 질 때와 이것이 발견되는 시간 간격이 길기 때문이다. 하지만, (코드가 변경될 때 마다) 테스트가 지속적으로 실행된다면 결함을 빠르게 발견할 수 있다.

본 기술자료에서는 다음 내용들을 다루고자 한다.

  • Ant에서 JUnit 실행하기
  • JUnit과 DbUnit을 사용하여 오래 실행되는 컴포넌트 테스트 실행하기
  • JUnitPerf를 사용하여, 실행하는데 너무 오래 걸리는 메소드 파악하기
  • Selenium으로 웹 기반 기능 테스트 실행하기
  • Cobertura로 코드 커버리지 측정하기
  • CruiseControl을 사용하여 지속적으로 테스트하기

나는 이 글에서, 다양한 유형의 개발자 테스트 개요와, 빌드 프로세스에 추가할 수 있고 Continuous Integration 시스템을 사용하여 지속적으로 실행할 수 있는 예제도 함께 제공하겠다.

JUnit을 사용한 단위 테스팅

단위 테스트(unit test)와 컴포넌트 테스트(component test)
종종 단위 테스트로 일컬어지는 이것은 컴포넌트 레벨의 테스트에 가깝다. 컴포넌트 테스트는 한 개 이상의 클래스를 검사하고, 데이터베이스 또는 파일 시스템에 의존성을 갖고 있다. 가장 중요한 것은, 컴포넌트 테스트가 단위 테스트 보다 실행하는데 더 오래 걸린다는 점이다.

가끔, 개발자들이 개발자 테스트(developer tests)라는 용어를 단순한 단위 테스트로 취급하는 것을 본다. 하지만, 단위 테스팅이라는 용어는 좀더 구체적인 것으로 다듬는 것이 좋다. 단위 테스트는 데이터베이스 같은 외부적 의존 관계를 갖고 있지 않는 개별 클래스를 테스트 하는 빠른 실행 테스트(fast running test)이다. 예를 들어, Listing 1은 JUnit을 사용하여 BeerDaoStub이라는 데이터 클래스를 확인하는 단위 테스트를 정의하고 있다. 실제로 데이터베이스에 연결되지 않은 인터페이스에 대한 테스팅 방식은 과도한 설정 비용을 들이지 않고 비즈니스 기능을 검사하는데 사용할 수 있는 방식이다. 게다가, 이렇게 하는 것이 진정한 단위 테스트이다.


Listing 1. 단위 테스트
public void setUp() {
  beerService = new BeerDaoStub();
}

public void testUnitGetBeer() {
  Collection beers = beerService.findAll();
  assertTrue(beers != null && beers.size() > 0);
}

단위 테스트를 작성했다면, IDE를 통해서 이들을 실행할 수 있지만, 빌드 프로세스의 일부로서 실행해야 할 것이다. 빌드 프로세스를 통해 테스트가 성공적으로 실행되는지를 확인한다는 것은 이와 같은 테스트들이 CI 빌드 정황에서도 시작될 수 있다는 것을 의미한다.

Listing 2는 일괄 단위 테스트를 실행하는 junit 태스크를 나타내는 Ant 스크립트이다. 이 태스크의 장점은 Junit과 함께 실행되고, 내가 정의했던 어떤 테스트도 이제는 자동으로 실행되며, 테스트들 중 하나라도 실패하면 haltonfailure 애트리뷰트를 사용하여 빌드도 실패로 끝나도록 한다.


Listing 2. Ant에서 단위 테스트 실행하기
<junit fork="yes" haltonfailure="true" dir="${basedir}" printsummary="yes">
  <classpath refid="test.class.path" />
  <classpath refid="project.class.path"/>
  <formatter type="plain" usefile="true" />
  <formatter type="xml" usefile="true" />
  <batchtest fork="yes" todir="${logs.junit.dir}">
    <fileset dir="${test.unit.dir}">
      <patternset refid="test.sources.pattern"/>
    </fileset>
  </batchtest>
</junit>  

test.unit.dir는 테스트의 위치를 정한다. 이는 이러한 테스트들을(이 경우, 단위(unit)) 다른 테스트들과 분리하는 효과적인 방법이다. 이 기술을 활용함으로써, 더 빠른 테스트를 먼저 실행할 수 있고, 추가 Ant 타겟을 정의하여 더 느린 테스트들(컴포넌트, 함수, 시스템 테스트)를 실행할 수 있다.




위로


컴포넌트 테스트 모으기

단위 테스트들은 너무 빠르게 실행되기 때문에 빌드의 일부로서 실행되기 쉬우며 해당 테스트는 코드 커버리지가 낮다. 이것의 고립성은 기능의 한 부분만 테스트 한다는 것을 의미한다. 더 많은 코드에 접근하여 더 많은 기능을 수행하는 테스트를 작성하는 것은 보조 프레임웍의 형태로 더 많은 조사를 필요로 한다. 이러한 헬퍼 프레임웍을 사용하여 테스트를 작성하기 시작한다면, 이러한 테스트들은 컴포넌트 테스트 범주에 들어갈 수 있다.

컴포넌트 테스트는 한 개 이상의 클래스를 확인하고 데이터베이스 같은 외부 의존 관계에 의존하는 테스트이다. 컴포넌트 테스트는 단위 테스트와 같은 방식으로 작성되며, 다만, 외부 종속 관계들과 고립을 조장하는 클래스들을 모방하는 대신 외부 의존성과 함께 작동하는 프레임웍을 리스팅 한다. 예를 들어, 나는 DbUnit 프레임웍을 사용하여 데이터베이스를 관리함으로써, 나의 컴포넌트 테스트는 데이터베이스의 데이터에 의존하는 코드의 기능을 확인할 수 있다.

DbUnit을 사용한 데이터베이스 상태 컨트롤

DbUnit은 데이터베이스에 대한 테스팅 프로세스를 훨씬 단순하게 만드는 프레임웍이다. 데이터베이스에서 데이터를 선택, 업데이트, 삽입, 제거하는데 사용될 수 있는 테스트 데이터를 정의하는 표준 XML 포맷을 제공한다. DbUnit은 데이터베이스를 대체하는 것이 아니며, 테스트 데이터를 핸들링 하는 보다 효율적인 메커니즘을 제공한다. DbUnit을 사용하여 특정 데이터에 의존하여 테스트를 작성할 수 있고, DbUnit에서 검사된 것이 기반 데이터베이스에 나타난다.

JUnit에서 프로그래밍 방식으로 DbUnit을 사용하거나, 빌드 프로세스의 일부로서 이것을 활용할 수 있다. 이 프레임웍에는 XML 파일을 사용하는 데이터베이스에서 데이터를 조작, 반출, 비교하는 방식을 제공한다. 예를 들어, Listing 3은 dbunit 태스크를 나타내고 있는데, 이 경우, 테스트 데이터를 대상 데이터베이스에 삽입하고 모든 컴포넌트 테스트를 실행한 후에 데이터를 제거한다.


Listing 3. Ant에서 컴포넌트 테스트 실행하기
<target name="component-tests">
  <mkdir dir="${logs.junit.dir}" />
  <taskdef name="dbunit" 
    classname="org.dbunit.ant.DbUnitTask"/>
    <dbunit driver="com.mysql.jdbc.Driver"
      url="jdbc:mysql://localhost:3306/brewery"
      userid="${db.username.system}"
	  classpathref="db.lib.path"
      password="${db.password.system}">
      <operation type="INSERT" 
        src="seedFile.xml"/>
    </dbunit> 
    <junit fork="yes" haltonfailure="false" 
	  failureproperty="tests.failed" 
	  haltonerror="true" dir="${basedir}"
	  printsummary="yes">
      <classpath refid="test.class.path" />
      <classpath refid="project.class.path"/>
      <formatter type="plain" usefile="true" />
      <formatter type="xml" usefile="true" />
      <batchtest fork="yes" todir="${logs.junit.dir}">
        <fileset dir="${test.component.dir}">
          <patternset refid="test.sources.pattern"/>
        </fileset>
      </batchtest>
    </junit>     
    <mkdir dir="${reports.junit.dir}" />
    <junitreport todir="${reports.junit.dir}">
      <fileset dir="${logs.junit.dir}">
        <include name="TEST-*.xml" />
        <include name="TEST-*.txt" />
      </fileset>
      <report format="frames" todir="${reports.junit.dir}" />
    </junitreport>
    <dbunit driver="com.mysql.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/brewery"
	    classpathref="db.lib.path"
        userid="${db.username.system}"
        password="${db.password.system}">
      <operation type="DELETE" 
          src="seedFile.xml"/>
    </dbunit>
</target>

Listing 3에서 보듯, 나의 컴포넌트 테스트는 실행하는 동안 데이터베이스에 있는 특정 데이터에 의존한다. 이 프로세스는 반복 가능하다. 모든 테스트를 성공적으로 실행한 후에 모든 데이터를 제거했기 때문이다.

데이터베이스 시드(seed)

dbunit 태스크에 INSERTDELETE 연산을 데이터베이스 테이블과 관련 행들을 나타내는 XML 엘리먼트들을 포함하고 있는 시드(seed) 파일들과 연결하여 사용할 수 있다. 예를 들어, Listing 4는 Listing 3에서 언급한 seedFile.xml 파일의 콘텐트이다. 각 BEER 엘리먼트는 BEER라고 하는 데이터베이스 테이블을 나타내고, 각 BEER 엘리먼트의 애트리뷰트와 이들의 값은 상응하는 데이터베이스 칼럼 이름과 값으로 매핑한다.


Listing 4. A DbUnit 시드 파일
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
  <BEER id='6' 
            beer_name='Guinness Extra Stout'			
            brewer='St.James Brewery'
            date_received='2007-02-01' />
  <BEER id='7' 
            beer_name='Smuttynose Robust Porter'			
            brewer='Smuttynose Brewery'
            date_received='2007-02-01' />
  <BEER id='8' 
            beer_name='Wolavers pale ale'	
            brewer='Wolaver Brewery'
            date_received='2007-02-01' />
</dataset>

Listing 3에서 보았듯이, DbUnit의 시드 파일들을 다른 연산에 재사용 할 수 있다. 내 경우, Listing 4의 파일을 사용하여 컴포넌트 테스트를 실행하기 전에 데이터베이스를 설치하고 같은 파일을 사용하여 테스트 완료 시 데이터베이스에서 어떤 데이터를 삭제할 것인지를 나타냈다.




위로


성능 테스트에 참여하기

성능 테스팅은 개발자가 코딩을 완료한 후에 오랫동안 수행된다. 하지만, 성능 문제는 개발 사이클에서 훨씬 더 일찍 발견된다. (그리고 해결된다.) 다행히도, 이 문제를 해결할 수 있는 한 가지 방법이 있다. 지속적인 테스팅 또는 보다 구체적으로는 JUnitPerf 테스트를 지속적으로 실행하는 것이다.

성능 테스팅의 완벽한 도구 JUnitPerf

JUnitPerf는 JUnit과 함께 작동하는 프레임웍으로서, 미리 정해진 시간 내에 테스트 케이스를 수행한다. 테스트의 방식이 임계치 보다 오래 걸리면, 테스트는 실패로 간주된다. 성능 테스트를 자동화 된 빌드로 통함함으로써, 애플리케이션 성능을 효과적으로 모니터 할 수 있고, 심지어 성능 문제가 나타난다면 구현을 실패로 할 수도 있다.

나는 실행 시간을 측정하는 메커니즘 보다 초기에 성능 문제를 발견하는 JUnitPerf를 사용하는 것을 더 선호한다. 프로파일러 같은 툴들은 이러한 유형의 측정을 훨씬 더 잘 수행한다. JUnitPerf를 초기 경고 시스템 정도로 생각해도 좋다.

Listing 5에서, JUnitPerf를 사용하여 BeerServicePerformanceTest 테스트 클래스에 있는 testLongRunningMethod라고 하는 테스트의 실행 시간을 검사하는 JUnit 테스트를 정의한다. 테스트 메소드 실행 시간이 1000 밀리초 이상 걸리면 실패한 것이다.


Listing 5. JUnitPerf를 사용한 성능 테스트
package com.beer.business.service;
import com.clarkware.junitperf.*;
import junit.framework.Test;

public class ExampleTimedTest {
  public static Test suite() {        
    long maxElapsedTime = 1000;        
    Test testCase = new BeerServicePerformanceTest("testLongRunningMethod");
    Test timedTest = new TimedTest(testCase, maxElapsedTime);        
    return timedTest;
  }
	
  public static void main(String[] args) {
    junit.textui.TestRunner.run(suite());
  }

}

메소드의 실행 시간에 대한 게이지로서 정밀한 타이밍을 사용할 때 주의하라. 테스트의 설정과 시간은 전체 실행 시간에 포함된다. 게다가, 실행 속도를 정밀하게 측정하는 것은 초기 성능 테스팅에 있어서 과학 보다는 예술에 가깝다.




위로


Selenium을 사용한 기능 테스트

여러분은 원하는 모든 단위 테스트와 컴포넌트 테스트를 작성할 수 있지만, 특정 유형의 사용자 인터페이스(예를 들어, 웹 애플리케이션)을 제공하는 애플리케이션을 작성한다면, 프레젠테이션 레이어를 테스트 해야 한다. 웹 애플리케이션의 경우, 사용자 시나리오의 흐름을 검사하고, 시나리오의 기능이 잘 수행되는지도 확인해야 한다. 최근까지, 이러한 유형의 테스팅은 일반적으로 부담이 되며, 후기 사이클 테스팅을 수행하는 툴을 구매해야 한다. 게다가, 이러한 툴은 테스트가 충분히 일찍 구현되더라도 빌드 프로세스에 잘 맞지 않는다.

Selenium

지난 몇 년 동안, 많은 오픈 소스 툴들이 기능 테스트 용도로 생겨나기 시작했다. 더욱이, 이러한 툴들을 개발 사이클의 초기에 사용할 수 있으며, Selenium과 Watir 같은 툴들은 오픈 소스들이 이들에 속한다. 게다가, 개발자들을 염두 해 두고 구현되었다. 다양한 언어(자바™와 Python)로 Selenium 테스트를 프로그래밍 방식으로 정의하는 것 외에도, Selenium은 배우기 쉬운 테이블 중심 포맷을 제공한다.

Selenium 프레임웍은 JavaScript를 사용하여 브라우저를 열고 테이블 중심 테스트를 실행하는 웹 기반 수락 테스트를 실행한다. 예를 들어, Listing 6은 간단한 Selenium 테스트를 나타내는 HTML 테이블을 나타낸다. 다양한 테스트 단계들이 웹 애플리케이션을 열고, 유효 사용자 이름과 패스워드로 로그인 한다. 테스트 결과는 HTML 테이블로 생성되어 Selenium이 모든 테스트 실행을 완료한 후에 볼 수 있다.


Listing 6. Selenium을 이용한 기능 테스트
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>MyTest</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
</thead><tbody>
<tr>
  <td>open</td>
  <td>/beer/</td>
  <td></td>
</tr>
<tr>
  <td>type</td>
  <td>username</td>
  <td>admin</td>
</tr>
<tr>
  <td>type</td>
  <td>password</td>
  <td>password</td>
</tr>
<tr>
  <td>clickAndWait</td>
  <td>//input[@value='Login']</td>
  <td></td>
</tr>
<tr>
  <td>verifyTextPresent</td>
  <td>Logged in as admin</td>
  <td></td>
</tr>
</tbody></table>
</body>
</html>

Listing 6에서 테이블 기반 포맷을 사용하여 여러 수락 테스트를 정의할 수 있다. 테스트들을 수트(suites)로 그룹핑 하여 한 번에 전체 수트를 실행할 수도 있다.

Ant를 통해 Selenium 구동하기

Selenium의 최대 장점은 CI로 처음부터 생성될 수 있다는 점이다. 왜냐하면 Ant 같은 빌드 툴에서 Selenium 테스트를 실행할 수 있기 때문이다. 더욱이, 프레임웍 디자이너들의 앞선 생각 덕택에, Selenium 수락 테스트가 실패하면, 전체 빌드도 무효로 할 수 있다. 예를 들어, Listing 7은 Selenium Remote Control 서버를 사용하여 웹 애플리케이션에 대한 테이블 중심 테스트를 실행하는 Ant 태스크를 나타내고 있다.


Listing 7. Ant에서 Selenium 실행하기
<?xml version="1.0" encoding="iso-8859-1"?>
<project name="functional-tests" default="run-selenium-tests" basedir=".">
  <property file="${basedir}/selenium.properties"/>
  <import file="${basedir}/common-environment.xml"/>
  <property name="acceptance.test.lib.dir"
                value="${functional.test.dir}" />
  <property name="firefox" value="*firefox" />
  <property name="base.url"
                value="http://${web.host.name}:${web.port}" />
  <property name="acceptance.test.list.dir"
                value="${functional.test.dir}" />
  <property name="acceptance.test.report.dir"
                value="${functional.test.dir}" />
  <target name="run-selenium-tests">
    <mkdir dir="${reports.dir}" />
    <java jar="${acceptance.test.lib.dir}/selenium-server.jar"
            fork="true">
      <arg line="-htmlSuite "${firefox}""/>
      <arg line=""${base.url}""/>
      <arg line=""${acceptance.test.list.dir}/${test.suite}""/>
      <arg line=""${reports.dir}/index.html""/>
      <arg line="-timeout ${timeout}"/>
    </java>
  </target>
  <target name="stop-server">
    <get taskname="selenium-shutdown" 
	      src="http://${web.host.name}:
		    ${selenium.rc.port}/selenium-server/driver/?cmd=shutDown"
      dest="result.txt" ignoreerrors="true" />
     <echo taskname="selenium-shutdown"
              message="Errors during shutdown are expected" />
  </target>
</project>

Selenium 테스트를 실행할 때, 프레임웍이 웹 브라우저를 열고, 빠른 속도로 테스트를 실행하고, 브라우저를 닫고, HTML 리포트를 만들어 낼 때 놀라지 말라. 이것은 개발 사이클에서 조기에 문제를 파악할 수 있는 보다 빠르고 쉬운 방법이다. (이들은 픽스하기도 더욱 쉽다.)




위로


Cobertura를 이용한 코드 커버리지

테스트들을 작성했다면 이제는 이 모든 테스트들이 무엇을 실행하는지를 결정할 차례이다. 다행히도, 이 질문에 대한 답은 Cobertura 같은 코드 커버리지 툴이 빛을 비추는 장소이다. 코드 커버리지 툴은 라인 또는 브랜치 커버리지 형태로 테스트 커버리지를 보고하여 테스트가 실행될 때 적용을 받는 코드의 양을 나타낸다.

Listing 8은 Cobertura를 사용하여 JUnit 테스트를 실행하여 얻은 코드 커버리지의 HTML 리포트를 만들어 내는 Ant 스크립트 모습이다.


Listing 8. Ant와 Cobertura를 사용한 코드 커버리지
<target name="instrument-classes">
  <mkdir dir="${instrumented.dir}" />
  <delete file="cobertura.ser" />
  <cobertura-instrument todir="${instrumented.dir}">
    <ignore regex="org.apache.log4j.*" />
    <fileset dir="${classes.dir}">
      <include name="**/*.class" />
      <exclude name="**/*Test.class" />
    </fileset>
  </cobertura-instrument>
</target>

<target name="run-instrumented-tests" depends="instrument-classes">
  <mkdir dir="${logs.junit.dir}" />
  <junit fork="yes" haltonfailure="true" dir="${basedir}" printsummary="yes">
    <sysproperty key="net.sourceforge.cobertura.datafile" file="cobertura.ser" />
    <classpath location="${instrumented.dir}" />
    <classpath location="${classes.dir}" />
    <classpath refid="test.class.path" />
    <classpath refid="project.class.path"/>
    <formatter type="plain" usefile="true" />
    <formatter type="xml" usefile="true" />
    <batchtest fork="yes" todir="${logs.junit.dir}">
      <fileset dir="${test.component.dir}">
	    <patternset refid="test.sources.pattern"/>
      </fileset>
    </batchtest>
  </junit>
</target>

100%냐 아니냐, 그것이 문제다.
Cobertura 또는 Emma 같은 툴을 실행할 때, 한 가지 기억해야 할 것이 있다. 특정 메소드에서 100% 라인 커버리지를 갖고 있다는 이유로, 이 메소드가 결함이 없다거나, 완전히 테스트 되었다는 것을 의미하지는 않는다. 예를 들어, 논리적 And를 포함하고 있는 if 문을 위한 테스트를 작성했고, 테스트가 식의 왼쪽 부분을 실행했다면, Cobertura 같은 툴은 100% 라인 커버리지를 리포트 해야 맞겠지만, 현실에서는 그 문의 50%만 실행한 것이다. 따라서, 50%의 브랜치 커버리지만 수행한 것이 된다.

Cobertura는 그림 1과 같은 HTML 리포트를 만들어 낸다. 라인과 브랜치 커버리지 백분율을 주목하라. 각 패키지를 클릭하여 클래스 레벨 라인과 경로 백분율을 알 수 있고, 어떤 소스 코드 라인들이 실행되었는지, 몇 회 실행되었는지를 알 수 있다.


그림 1. Cobertura와 Ant를 사용하여 생성된 HTML 리포트
그림 1. Cobertura와 Ant를 사용하여 생성된 HTML 리포트
얼마나 많은 코드 커버리지가 필요한가?
이상적으로는 각 경로에 하나의 테스트가 있어야 한다. 다시 말해서, 전체 코드 베이스가 20,000의 순환 복잡도(cyclomatic complexity)를 갖고 있다면, 20,000개의 테스트가 있어야 한다. 100%에 근접한 라인 커버리지를 가진 팀을 보았지만, 100%의 경로 커버리지를 가진 프로젝트는 한 번도 본 적이 없다.

다양한 유형의 테스트와 그러한 테스트의 커버리지를 측정하는 방법에 대해 알아보았다. 이러한 테스트들의 실행이 정기적인 간격으로 이루어진다는 것을 어떻게 확인할까? CruiseControl 같은 CI 서버가 이 문제를 해결한다.

테스트를 지속적으로 실행하기

다양한 개발자 테스트 유형을 빌드 프로세스에 일단 적용했다면, CI 프로세스의 일부로서 이러한 테스트들의 일부(또는 전체)를 실행할 수 있다. 예를 들어, Listing 9는 CruiseControl의 config.xml 일부이고, 많은 것들을 정의했다. 우선, 나는 CruiseControl이 2 분 마다 발생하는 변경 사항에 대해 Subversion 저장소를 모니터링 하도록 했다. 이것이 변경 사항을 발견하면 CruiseControl은 build-${project.name}.xml이라고 하는 빌드 스크립트(Ant로 작성됨)를 위임하기 시작한다. 위임을 받은 빌드 스크립트는 프로젝트의 빌드 스크립트를 호출하고, 이것은 컴파일을 실행하고 테스트를 실행한다.

나는 또한 다양한 유형의 테스트 결과들을 CruiseControl 로그 파일에 결합하는 로직도 정의했다. 또한, CruiseControl의 기능을 사용하여 (artifactspublisher 태그를 사용하여) 다른 툴에 의해 생성된 리포트를 Build Artifacts 링크로 연결했고, 이것은 CruiseControl 대시보드 애플리케이션에서 사용할 수 있다.


Listing 9. CruiseControl을 이용한 CI
...
<modificationset quietperiod="30">
  <svn RepositoryLocation="http://your-domain.com/trunk/brewery"
    username="bfranklin"
    password="G0Fly@Kite"/>
</modificationset>
<schedule interval="120">
  <ant anthome="apache-ant-1.6.5" buildfile="build-${project.name}.xml"/>
</schedule>
<log dir="logs/${project.name}">
  <merge dir="projects/${project.name}/_reports/unit"/>
  <merge dir="projects/${project.name}/_reports/component"/>
  <merge dir="projects/${project.name}/_reports/performance"/>
  <merge dir="projects/${project.name}/_reports/functional"/>
  <merge dir="projects/${project.name}/_reports/coverage"/>
</log>
<publishers>
  <artifactspublisher 
    dir="projects/${project.name}/_reports/" 
    dest="projects/artifacts/${project.name}"/>
</publishers>
...

버전 컨트롤 저장소에 적용된 모든 소스 변경에 대해 모든 테스트를 실행할 필요는 없다. 예를 들어, CI 시스템을 설정하여 코드 체크인(커밋(commit) 빌드) 동안 단위 테스트만 실행하는 빌드를 실행할 수 있다. 커밋 빌드를 컴포넌트 테스트, 기능 테스트, 성능 테스트, 코드 검사를 실행하는 빌드로 보완할 수 있다. (참고자료) 이러한 빌드는 빈도수가 적게 실행된다. (하루에 한번) 또는, 이러한 테스트와 검사를 커밋 빌드 후에 바로 실행할 수도 있다.




위로


모든 테스트 호출하기

연속 테스팅은 대역폭과 주파수를 포함한다. 다양한 유형의 테스트를 작성함으로써, 광범위한 커버리지와 정확성을 얻을 수 있다. 더욱이, 이러한 테스트들을 연속적으로 실행함으로써, 문제들이 만들어지는 속도만큼 빨리 찾아낼 수 있다.

단위 테스팅 자체로는 프로젝트를 이끌어 갈 수 없다. 더 높은 코드 커버리지를 달성하고 팀의 정확성을 높이려면 자동화된 컴포넌트 테스트, 성능 테스트, 기능 테스트를 결합 및 실행하는 노력도 필요하다. 더욱이 프레임웍과 JUnit, Selenium, Cobertura 같은 툴을 사용하면 빌드 자동화를 쉽게 이룩할 수 있고, CI 시스템의 도움을 받아서 버전 컨트롤 저장소에 변경이 이루어질 때 마다 테스트 수트를 효과적으로 실행할 수 있다.

기사의 원문보기



참고자료

교육

제품 및 기술 얻기

토론


필자소개

Paul Duvall은 Stelligent Incorporated의 CTO이다.UML™ 2 Toolkit Toolkit을 공동 집필했으며 곧 출간될 Addison-Wesley Signature Series, Continuous Integration: Improving Software Quality and Reducing Risk의 공동 저자이다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us





위로


Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. 기타 회사, 제품, 및 서비스명은 다른 상표나 서비스 마크일 수 있습니다.

developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의