메이븐은 빌드 시 pom.xml에 기술된 의존관계를 분석하여, 빌드에 필요한 jar 파일을 자동으로 아티팩트 레포지토리에서 다운로드 받아, 사용자의 로컬 레포지토리에 설치해줍니다. 그런데 가끔은 jar 파일을 수동으로(커맨드라인에서) 다운받고 싶을 때가 있습니다.

당연히 이런 기능을 제공하는 메이븐 플러그인이 있을 것으로 짐작하고 찾아보지만 잘 안보입니다. 좀더 집요하게 검색해보니 2개 정도 나옵니다.

1. http://www.agaetis.fr/public/maven-download-plugin/

2. http://maven.apache.org/shared/maven-downloader/

1번 플러그인은  mvn download:download -Dartifact=group:artifact:version:jar 식으로 실행하면 된다고합니다. 하지만 이 플러그인은 메이븐 메인레포지토리에 아직 등록되지 않았고, http://mvnrepository.com/ 같은 플러그인 검색사이트에서 검색해도 안나오는걸 보면 다른 공공(?) 레포지토리에도 등록하지 않은 듯합니다. 게다가 JDK6을 요구하기 때문에 그 이하 버전의 JDK 환경(저처럼)에서는 실행되지 않는 문제가 있습니다.

2번 플러그인은 메이븐 공식 플러그인 같기도합니다만 실행가능한 goal 등 어떻게 사용하면 되는지 설명이 전혀 없습니다. 그리고 실제로 mvn org.apache.maven.shared:maven-downloader:download 이렇게 실행해보면 일단 플러그인은 다운로드가 되지만 PluginDescriptor 에러가 납니다.

java.lang.IllegalStateException: The PluginDescriptor for the plugin Plugin [org.apache.maven.shared:maven-downloader] was not found.

에혀 이런저런 삽질이 모두 물거품이 되었네요. 구관이 명관인가요. 결국 아티팩트 다운로드 전용 pom.xml을 만드는 것으로 합의(?)를 봤습니다.

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>download</groupId>
     <artifactId>download</artifactId>
     <version>1.0</version>
     <packaging>jar</packaging>
     <name>DOWNLOAD ARTIFACTS</name>
     <description>for artifact downloads(다운로드 받을 라이브러리 지정후 mvn dependency:resolve 실행)</description>

     

  2.  <dependencies>
      <dependency>
       <groupId>다운로드 받을 라이브러리의 groupId</groupId>
       <artifactId>다운로드 받을 라이브러리의 artifactId</artifactId>
       <version>다운로드 받을 라이브러리의 version</version>
      </dependency>
     </dependencies>
  3. </project>

이렇게 pom.xml을 만들어두고 필요시마다 goupId, artifactId 등을 수정하여 mvn dependency:resolve 를 실행하면 됩니다. 테스트해보니 잘되네요. 이정도에서 만족해야겠습니다. ^^;

이 글은 스프링노트에서 작성되었습니다.

Posted by 에코지오
,

셀레늄IDE는 레코딩된 html 테스트케이스를 자바 코드로 변환해주는 놀라운 기능을 제공합니다.

그러나 이렇게 자동으로 만들어진 JUnit3 기반 테스트케이스 코드는 약간 아쉬운 점이 있습니다.

  • 테스트케이스 실행전 먼저 셀레늄 RC 서버를 별도로 띄워놓아야 한다.
  • 셀레늄IDE에서 제공하는 clickAndWait와 같은 xxxAndWait 류의 커맨드에 해당하는 메소드가 없다.
  • JUnit 4 나온지가 언젠데 아직도 JUnit 3 이냐. -.-
  • 하나의 테스트메소드 실행시마다 브라우저 열기/닫기를 반복하기 때문에 테스트 수행시간이 오래걸린다. (테스트케이스의 setUp과 tearDown에 브라우저를 띄우고 종료하는 코드가 있음)

다행히 누가 시킨것도 아닌데 이런 아쉬움을 극복하는 방법을 정리해서 올리신 분들이 계십니다.

먼저 http://ajesse.wordpress.com/category/selenium 사이트.

위 사이트에는 셀레늄RC서버 자동시작/종료, clickAndWait류의 메소드제공, JUnit4기반코드, 테스트케이스 단위로 브라우저를 열고닫는 등 모든 해법이 설명되어 있습니다.

테스트스위트에서는 셀레늄RC서버를 관리합니다.

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

  2. public abstract class SeleniumTestSuite {

  3.     @BeforeClass
        public static void setUpBeforeClass() throws Exception {
            SeleniumServerControl.getInstance().startSeleniumServer();
        }

  4.     @AfterClass
        public static void tearDownAfterClass() throws Exception {
            SeleniumServerControl.getInstance().stopSeleniumServer();
        }
    }

 

공통 테스트케이스 클래스는 브라우저를 콘트롤하는 기능과 xxxAndWait 유틸리티 메소드를 제공합니다.

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

  2. import com.thoughtworks.selenium.DefaultSelenium;
    import com.thoughtworks.selenium.Selenium;

  3. public abstract class SeleniumTestCase {

  4.     public static final String BROWSER = "*iexploreproxy";

  5.     public static final int SELENIUM_SERVER_PORT = 4444;

  6.     public static final String PAGE_LOAD_TIMEOUT = "3000";

  7.     public static final String START_URL = http://myserver.com:xxxx;

  8.     protected static Selenium seleniumStatic;

  9.     protected Selenium browser = null;

  10.     public SeleniumTestCase() {
            this.browser = seleniumStatic;
        }

  11.     @BeforeClass
        public static void setUpBeforeClass() throws Exception {
            seleniumStatic = new DefaultSelenium("localhost", SELENIUM_SERVER_PORT, BROWSER, START_URL);
            seleniumStatic.start();
        }

  12.     @AfterClass
        public static void tearDownAfterClass() throws Exception {
            seleniumStatic.stop();
        }

  13.     public void submitAndWait(String formLocator) {
            browser.submit(formLocator);
            browser.waitForPageToLoad(PAGE_LOAD_TIMEOUT);
        }

  14.     public void clickAndWait(String elementLocator) {
            browser.click(elementLocator);
            browser.waitForPageToLoad(PAGE_LOAD_TIMEOUT);
        }

  15.     public void openAndWait(String url) {
            browser.open(url);
            browser.waitForPageToLoad(PAGE_LOAD_TIMEOUT);
        }

추가로 JUnit4 사용환경에서 테스트 실패시 자동으로 화면을 캡쳐하는 방법에 대한 힌트가 http://rockhoppertech.com/blogs/archives/45 여기에 언급되어 있습니다. JUnit4에 org.junit.runner.notification.RunListener 클래스가 있네요.

 

ps. 이 글을 정리하면서 Simplium 이라는 것을 발견했습니다. Simple test framework for Selenium의 약자이군요. 사이트에 소개된 코드를 대략 보니 일단 맘에 듭니다. 시간나면 Simplium을 좀더 테스트해봐야겠습니다.

이 글은 스프링노트에서 작성되었습니다.

Posted by 에코지오
,

입문자로써 셀레늄을 처음 접했을 때, 셀레늄은 뭐랄까 사람을 좀 헷갈리게 만드는 것같습니다.

셀레늄 다운로드 사이트에 나열된 Selenium Core, Selenium RC, Selenium IDE, Selenium Grid 등등을 보면서 뭐부터 다운받아야하는지 고민이 됩니다. 저는 다행히 이 단계는 지났습니다. ^^;

또하나 아리까리한 것이 있습니다. html로 테스트할지, java 코드로 테스트할지.

셀레늄 IDE는 테스트케이스를 HTML로도 만들어주고 Java 코드로도 만들어줄 수 있습니다. 그래서 어떤 포맷으로 테스트케이스를 만들지 또 고민해야합니다.

좀 고민하다가 HTML기반 테스트케이스와 Java(JUnit) 기반 테스트케이스를 간단히(정말 간단히) 비교해봤습니다.


HTML 기반 테스트

  • 일반 사용자가 이해하기가 쉽다.
  • 약간의 교육으로 테스트케이스를 쉽게 작성할 수 있다.
  • 컴파일이 필요없다.
  • 특별한 툴없이 HTML을 직접 수정할 수 있다. 그러나 셀레늄IDE를 통해 편집하는게 편하다.
  • 조건에 따라 명령어를 동적으로 수행하는 등의 프로그램적 요소가 부족하다.
  • FireFox를 지원하지 않는 웹사이트의 경우 테스트케이스 작성이 어렵다.(셀레늄IDE는 firefox만 지원)
  • selenese Ant 타스크를 통해 자동으로 테스트스위트를 실행할 수 있다.

 

자바(JUnit) 기반 테스트

  • 이클립스와 같은 개발도구의 기능을 충분히 이용할 수 있다.
  • 조건에 따라 동적으로 테스트 흐름을 조작할 수 있다.
  • 공통적인 부분을 뽑아내기가 좋다.
  • 컴파일이 필요하다.
  • 이클립스에서 바로 실행가능하다. 초록막대.
    셀레늄IDE가 생성해주는 자바코드의 품질이 떨어진다.
  • 셀레늄IDE가 생성해준 자바코드는 JUnit 3.x 기반이다.
  • verify나 assert 실패시 자동으로 화면을 캡쳐하는 등 기능을 추가할 수 있다.
  • 테스트 메소드 하나씩 실행시마다 브라우저 open/ close를 반복하므로 상대적으로 느릴 수 있다.(개선방법 있음)
  • junit Ant 타스크를 통해 자동으로 실행할 수 있다.

이 글은 스프링노트에서 작성되었습니다.

Posted by 에코지오
,

셀레늄에서 자바스크립트 alert 상자가 떴는지 판단하는 명령어에는 verifyAlert, verifyAlertPresent, assertAlert, assertAlertPresent 등이 있습니다.

셀레늄은 이들 명령어를 통해 alert 상자가 뜬 것을 확인하고(실제로 뜨지는 않습니다) 자동으로 확인버튼을 눌러줍니다.

셀레늄 IDE에서 명령어(command) 항목에는 assertAlert 를,  타겟(target) 항목에는 "저장하였습니다."를 입력하면, 실제로 "저장하였습니다." alert 상자가 뜰 경우에 테스트가 성공하게 됩니다.

b.jpg 

그런데 제가 테스트하는 화면에서는 alert의 메시지가 중요하지 않아서 단순히 alert가 발생했는지 여부만 알고 싶을 때가 있습니다. 그래서 assertAlert 명령어 대신 assertAlertPresent 명령어를 사용해봤는데, 아래처럼 에러가 납니다. verifyAlertPresent 명령도 마찬가지였습니다.

[error] Error: There was an unexpected Alert! [저장하였습니다.]

뭐가 잘못된 건지 구글신께 물어봐도 잘 안가르쳐 주네요... -.-;

이리저리 삽질하다가 assertAlert 명령에서 메시지 지정할 때 * 패턴을 사용할 수 있다는 사실을 알아냈습니다. 그래서 찾아낸 방법은 아래 그림처럼 assertAlert 명령의 target 값으로 * 를 쓰는 것입니다. 잘 작동합니다. ^^

a.jpg 

이 글은 스프링노트에서 작성되었습니다.

Posted by 에코지오
,

셀레늄에는 assert 뿐만 아니라 이와 유사한 verify 명령어가 있습니다.

 

  • verify : 중간에 실패해도 나머지 테스트는 계속 수행된다. 마지막에 실패가 표시된다.
  • assert : 중간에 실패하면 테스트는 즉시 중단된다.

 

verify는 실패하더라도 그 다음 테스트명령 실행에 영향을 주지 않으면서 무언가를 확인하고 싶을 때 사용하면 되겠고,

assert는 실패하면 다음 명령을 수행하는게 의미가 없어서 바로 테스트를 실패로 단정짓고 싶을 때 사용하면 되겠습니다.

 

 

이 글은 스프링노트에서 작성되었습니다.

Posted by 에코지오
,

최신 셀레늄 RC 베타2에서 브라우저 별칭이 바뀐 모양입니다.

갑자기 셀레늄으로 테스트해 볼일이 생겨 최신버전으로 설치하고 매뉴얼/블로그 따라해보니 잘 안되네요.

삽질 끝에 브라우저 별칭을 잘못 지정한 것을 알게되었습니다.

이전 버전의 *iexplore는 *iexploreproxy로, *firefox는 *firefoxproxy로 바뀌었습니다.

 

 

LAUNCHER NAME          DESCRIPTION                    CROSS-DOMAIN

--------------------------------------------------------------------
*iexplore, *iehta  =>  Internet Explorer in HTA mode  YES
*iexploreproxy     =>  Internet Explorer normal           NO
*firefox, *chrome  =>  Firefox in Chrome mode         YES
*firefoxproxy        =>  Firefox normal                        NO

 

더 자세한 내용은 다음 사이트를 참조하세요.

http://clearspace.openqa.org/community/selenium/blog/2009/01/13/selenium-rc-beta-2-goodies-and-gotchas

이 글은 스프링노트에서 작성되었습니다.

Posted by 에코지오
,

1. hex string -> byte array

byte[] bytes = new java.math.BigInteger(hexText, 16).toByteArray();


2. byte array -> hex string

String hexText = new java.math.BigInteger(bytes).toString(16);


아래처럼 머리 아픈 코드를 안봐도 되니 편하네요. thanks BigInteger! ^^

    public static String toHexString(byte buf[]){
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            sb.append(Integer.toHexString(0x0100 + (buf[i] & 0x00FF)).substring(1));
        }
        return sb.toString();
    }

Posted by 에코지오
,
Posted by 에코지오
,
Hudson을 통해 buildr 프로젝트를 빌드해보겠습니다.(블로깅이 너무 뜸한거 같아 의무방어.. -.-)

1. New Job 화면에서 프로젝트 유형으로 Build a free-style software project 를 선택합니다.



2. 추가된 Job 설정 화면의 Build 항목에서 Add build step 버튼을 눌러 Execute Windows batch command를 선택합니다.



3. Command 필드에 명령 프롬프트에서 입력하듯이 buildr 명령을 입력합니다.



4. 나머지는 평소 설정하신대로 설정하시면 됩니다.

5. Build Now를 눌러 빌드가 잘 되는 것을 확인할 수 있습니다.


간단하네요. ^^
Posted by 에코지오
,

이제 Buildr 시리즈도 막바지를 향해 달려가는군요. 이번에는 빌드 결과물을 원격의 서버에 배포해보겠습니다.
Maven에 install, deploy 단계(phase)가 있는 것처럼 Buildr에도 배포와 관련된 install, upload, release 타스크가 있습니다만, 일반적인 웹어플리케이션 개발 프로젝트에서는 그닥 쓸모가 없습니다. 웹어플리케이션 프로젝트에서 배포는 대부분 WAS에 deploy하는 것을 의미하기 때문입니다.

로컬 WAS에 배포하는 건 간단하니 넘어가고, 리모트에 있는 개발서버나 운영서버에 우리가 빌드한 어플리케이션을 전송하는 방법을 알아보겠습니다.

요즘은 원격 서버에 연결하기 위한 프로토콜로 거의 ssh, scp를 이용합니다. Ant에서는 ssh,scp 프로토콜로 원격 서버에 연결하기 위해서 흔히 jsch 라이브러리를 사용했죠. Buildr에서는 net-ssh, net-scp 라이브러리를 사용하여 이런 작업을 처리합니다. 

먼저 profiles.yaml에 다음처럼 WAS 서버에 대한 접속정보가 설정되어 있습니다(이건 예제입니다).

development:
  wasserver:
    host: 111.111.111.111
    username: weblogic
    password: weblogic
    deploy_dir: /home/weblogic/webapps
    restart: sh /home/weblogic/restart.sh

production:
  wasserver:
    host: 222.222.222.222
    username: prod
    password: prod
    deploy_dir: /home/prod/webapps
    restart: sh /home/prod/restart.sh

WAS 서버에 웹어플리케이션을 업로드하는 타스크는 아래와 같이 작성할 수 있습니다. Net::SCP.start 메소드에 접속정보를 넘겨주고 블록 안에서 scp의 upload 메소드를 이용하여 'target/webapp' 디렉토리를 업로드하고 있습니다.

task :deploy_webapp => :package do
  require 'net/ssh'
  require 'net/scp' 

  wasserver = Buildr.settings.profile['wasserver']
  Net::SCP.start(wasserver['host'], wasserver['username'],
                       :password => wasserver['password']) do |scp|
    scp.upload!('target/webapp',  wasserver['deploy_dir'], :preserve=>true, :recursive=>true) 
  end
end

이렇게 업로드하면 로컬의 target/webapp/index.jsp 파일은 원격의 /home/weblogic/webapps/webapp/index.jsp에 위치하게 됩니다. 그러니까 로컬의 webapp 디렉토리 자체가 원격 타겟 디렉토리 밑으로 들어갑니다. 아 이건 우리가 원하는게 아닙니다. target/webapp 디렉토리는 빼고, 그 밑의 파일/디렉토리들만 원격 타겟 디렉토리 밑으로 보내고 싶습니다. 고민할 필요없이 FileList를 이용하면 됩니다.

FileList['target/webapp/*'].each do |file|
  scp.upload! file, file.sub('target/webapp',wasserver['deploy_dir']),:preserve=>true,:recursive=>true
end

exploded 모드로 빌드된 target/webapp 디렉토리 말고 그냥 하나의 패키징된 war 파일을 업로드하는 건 어떨까요? 

  scp.upload! package(:war).name, wasserver['deploy_dir'], :preserve=>true

패키징된 war 파일의 이름은 package(:war).name 로 구할 수 있었습니다. 무지 쉽군요. 

실제 실행해보시면 알겠지만, 이렇게 원격으로 업로드 시 콘솔에는 아무 것도 찍히지 않습니다. 재미가 없죠. 지금 어떤 파일이 전송되고 있는지 최소한 파일 이름이라도 나와주면 좋을 겁니다. 업로드 진행현황은 upload 메소드에 블록을 넘겨주어 표현할 수 있습니다. 다음은 업로드되는 파일의 이름과 파일의 전체 크기를 출력하는 예제입니다.

scp.upload!(package(:war).name, 
                 wasserver['deploy_dir'],
                 :preserve=>true) do |ch, name, sent, total|
  puts "uploading #{name} (#{total} byte)" if sent == 0
end

음... 전송파일의 크기가 큰 경우에는 콘솔에 변화가 별로 없으니 이것도 솔직히 별로 재미가 없습니다. 좀더 다이내믹하게 진짜로 현재의 업로드 진행 현황을 막대그래프에 #이나 * 문자가 늘어나는 식으로 보여주면 좋겠는데....
Buildr에서 메이븐 저장소에서 아티팩트를 다운로드 받을 때 다운로드 그래프가 나온다는 사실을 떠올리며 Buildr 소스를 여기저기 까보다가 ProgressBar 클래스를 발견했습니다. 나름 쓸만하네요.

local_file = package(:war).name
remote_dir = wasserver['deploy_dir']

ProgressBar.start :total=>File.size(local_file),:title=>local_file do |progress|
  scp.upload!(local_file, remote_dir, :preserve=>true) do  |ch, name, sent, total|
    progress.inc sent
  end
end

콘솔에 이렇게 출력됩니다.


웹어플리케이션을 업로드했으니, WAS를 재기동해볼까요? 서버에 있는 WAS를 재기동 or 웹어플리케이션을 재배포하는 여러 방법중에서 가장 간단한 방법은 아마도 쉘스크립트를 실행하는 방법일 겁니다. 원격 명령어 실행은 Net::SSH를 이용하여 처리합니다. Net::SCP와 유사하니 설명은 생략합니다.

task :restart_was do
  require 'net/ssh' 

  wasserver = Buildr.settings.profile['wasserver']
  Net::SSH.start(wasserver['host'],
                 wasserver['username'],
                 :password => wasserver['password']) do |ssh|
    ssh.exec! wasserver['restart']
  end
end

만약 WAS 재기동 스크립트 실행을 :deploy_webapp 타스크 안에서 처리하고 싶으면 다음처럼 합칠 수 있습니다. (ssh.scp로 쓰인 부분을 주목하세요)

task :deploy_webapp do
  require 'net/ssh'
  require 'net/scp'
  wasserver = Buildr.settings.profile['wasserver']
  Net::SSH.start(wasserver['host'],
                 wasserver['username'],
                 :password => wasserver['password']) do |ssh|    
    ssh.scp.upload! package(:war).name, wasserver['deploy_dir'], :preserve=>true     
    ssh.exec! wasserver['restart']
  end
end

 

'Build&Deploy > Buildr' 카테고리의 다른 글

Buildr : Hudson CI 연계하기  (0) 2009.01.14
Buildr : 설정정보 관리  (0) 2008.12.24
Buildr : 코드분석 리포트 생성  (0) 2008.12.23
Buildr : 빌드결과 패키징  (0) 2008.12.18
Buildr : 아티팩트 다운로드 받기  (0) 2008.12.17
Posted by 에코지오
,