반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형

새로 투입된 프로젝트에서 어제 드디어 노트북을 받았습니다.


받자마자 필요한 프로그램 깔고 세팅하고 등록할 시스템에 등록하고 permission들 받고 정신없이 세팅 했는데.


오늘부터 당장 일이 주어지네요.

컴퓨터만 주어지면 그냥 일을 시작해야 되는 그런 분위기입니다.


이번 프로젝트는 제가 테스팅 팀에서 일하게 됐는데요. 이쪽 회사는 테스트와 관련된 체제가 아주 잘 돼 있는 것 같습니다.


일단 다음에 이런 류의 프로젝트에 투입돼서도 처음 하는 일은 개발환경 세팅이고 이게 거의 유사할 테니까 한번 간략하게 나마 정리해 두면 나중에 도움이 될 것 같네요.



인터뷰 합격에서 프로젝트 조인까지


일단 합격에서부터 조인까지 한번 정리해 보자면요.

지난번 프로젝트 끝나고 일이 꼬이는 바람에 갑자기 Bench 에 있게 됐습니다.

(컨설팅 회사에서는 대개 고객 회사로 가서 (On Site) 그쪽 프로젝트를 컨설팅 해 주는 방식으로 일이 진행됩니다. 그러다 보면  프로젝트와 프로젝트 사이에 특별히 일이 없는 때가 있을 수 있는데요.

이런걸 Bench 에 있는다고 말합니다.

원래는 그런 기간 없이 일을 진행합니다. 그리고 웬만해서는 Bench를 허락하지 않구요.

그런데 이번에는 일이 좀 꼬여서 그렇게 됐습니다.


하지만 지난 프로젝트가 9월말에 끝나고 새로운 프로젝트에 10월 초에 합격했으니 실질적으로 Bench에 있는 기간은 많지 않았습니다.


이곳에서는 합격을 하더라도 그 사람에 대해 Background check를 진행합니다.

보통 1주일 걸리는데요. 주로 전과기록이나 현재 범죄자로 수배중인지 혹은 금전적으로 파산이나 부도 등의 안좋은 기록들이 있는지 뭐 이런걸 조사하는 것 같더라구요. (정확하지는 않습니다)

저의 경우는 한국쪽 기록도 검사한다면서 제 기록을 회사에 제공해도 좋다는 각서를 받아가더라구요.

그래서 시간이 더 걸렸는데요. 그래서 한 3주 가까이 기다렸습니다.

아마 중간에 미국 정부 Shutdown 이 있어서 그 영향도 있지 않았나 짐작만 해 봅니다.


Background Check 가 Green 으로 평가 됐다는 연락을 받자마자 새로 조인할 프로젝트 매니저한테 연락했더니 저에게 1주일 시간을 주더라구요. 미국 남서부에서 중북부로 이사 하는 시간을요. 거의 2000마일 이니까 3200 키로미터 정도 됩니다.



짐 나르는거는 2500불 자동차는 1000불 정도 들었습니다.

이 돈은 나중에 영수증 첨부해서 회사로부터 받을 수 있습니다.


이렇게 해서 첫 출근을 한것이 11월 4일 월요일 입니다.

가자마자 일을 시작하는 건 아니고 또 SSO (회사 내부 시스템을 이용할 수 있는 권한)를 받아야 합니다.

그래야지 자리도 정해지고 노트북 컴퓨터도 나오고 전화기도 한대 주어지고 하거든요.

그 SSO와 노트북을 어제 (13일)에 받았습니다.

월요일이 Memorial Day 로 공휴일이 었으니까 그거 받는데 일주일 조금 넘게 걸릴거네요.


이제 SSO 가 나왔으니까 Desk가 정해지고 전화기가 나오고 제 사진이 박힌 회사 출입증이 나올 수 있습니다.

Desk는 대충 빈자리에 앉으면 되니까.. 일단 노트북을 받았으니까 일은 시작할 수 있게 됐습니다.




개발 환경 세팅 하기


일을 시작하기 전에 일을 시작할 수 있도록 여러가지 툴들을 깔고 내부 시스템에 등록하고 필요한 권한을 받고 해야 합니다.


먼저 자바 개발자이니까 제일 먼저 JDK 를 깝니다. 


그 다음에는 Eclips 를 J2EE 용으로 다운 받아서 설치 합니다.

최신 버전을 받았는데 요즘 최신버전은 Kepler 더군요.

그리고 그 Eclips에 필요한 플러그인을 설치합니다.


TestNG - 테스트를 작성할 때 framework으로서 사용합니다. 이것을 사용하면 Report 결과를 쉽게 볼 수가 있고 Data Driven 테스트 케이스 작성되 가능합니다.


Maven - Build Tool 이죠. Compile, Pack, Test (integration), Deploy 등을 자동으로 할 수 있게 해 줍니다. 그리고 Dependency Management 기능도 있어서 필요한 jar 파일들을 이클립스에 자동으로 import 해 줍니다.


Selenium Webdriver - TestNG와 함께 사용해서 테스트 케이스를 만듭니다. 실제 로직은 이 Selenium 으로 작성합니다.


Subeclipse - svn 이죠. 버전 관리 툴 입니다.


이 정도가 주요 툴 입니다.



그리고 Client 측에서 만든 Framework이 있는데요. 이 Framework은 Selenium 을 좀 더 편하게 사용할 수 있도록 만든 Framework 입니다.


그리고 또 다른 툴로는 ALM 이 있더라구요.

ALM 은 Application Lifecycle Management 로 이 프로젝트에서는 HP것을 사용하고 있습니다.


그 다음에 Continues integration 툴인 Jenkins를 사용하구요. 

이건 서버 역할을 하는 거니까 제 로컬에는 깔지 않구요. 그냥 접속 권한만 받았습니다.


그리고 개발자들이 사용하는 Portal 시스템에 등록도 했구요.


그리고 아직 등록은 하지 않았는데 원격에서 시스템을 사용할 수 있는 권한인 VPN을 이용하기 위해 Token 도 받아야 합니다.


이렇게 겨우 일 시작할 수 있을 정도로 세팅을 해 놓으니까 곧바로 일을 주더라구요.


오자마자 두개의 프로젝트를 뛰라고 지시 받았습니다.


Main은 Oracle에서 Teradata 로 Migration 작업을 하는 프로젝트에서 데이터 비교 검증하는 테스트일입니다.

그리고 제가 support 하여야 할 프로젝트에서는 Selenium Webdriver를 사용해서 UI 를 테스트 하는 겁니다.


아직 Main 작업을 들어가지 않았는데 UI 테스트 쪽 support 하면서 하루에 테스트 케이스 10개씩 만들라고 할당 받았습니다. (뭐 익숙해지만 하나도 안 많은 거지만.. 아직 익숙하지 않은 툴들도 있고 이쪽 시스템도 제대로 분석 못했는데...)


뭐 초보자로 들어와서 일을 배워가면서 해야 되는것도 아니고..

이쪽 SI 시장이 그렇죠 뭐. 

일이 많으면 얘기하라고 하더라구요. 그러면 인도쪽 offshore 팀 하나 만들어 준다구요.

그러면 매일 밤마다 그쪽이랑 미팅하고 해야 되는데...


일단 저부터 이쪽 일을 어느정도 파악 한 다음에 생각해 봐야겠습니다.

반응형

Jenkins 에 대한 개요

2013. 11. 8. 11:02 | Posted by 솔웅


반응형

What is Jenkins?



Jenkins는 반복되는 job들에 대한 실행을 모니터 하는 award-winning application 입니다. 예를 들어 소프트웨어 프로젝트를 빌드한다던가 cron에 의해 실행되는 job들 같은 경우 이지요. 그런 일들 중에 현재 Jenkins는 아래 두가지 job들에 포커스를 맞추고 있습니다.

    Building/testing software projects continuously, just like CruiseControl or DamageControl. Jenkins는 지속적으로 integration이 진행되는 시스템 에서 이러한 일들을 쉽게 사용할 수 있도록 해 줍니다. 개발자들이 프로젝트를 진행하면서 이루어 지는 integrate change들을 쉽게 진행할 수 있도록 해 주는 것이죠. 그리고 빌드 하는 작업도 쉽게 진행하도록 도와 줍니다. 자동화되고 지속적인 빌드는 생산성을 높여줍니다.
 
    cron job들과 procmail job들 같은 외부에서 실행되는 job들에 대한 모니터링을 합니다. 원격에서 실행되는 job들에 대한 모니터도 가능합니다. 예를 들어 cron의 경우 output을 capture 한 정기적인 이메일을 받는 일등이 있을 수 있습니다. 여러분은 정기적으로 이 이메일을 점검함으로서 뭔가 이상이 있을 때 이것을 알아챌 수 있는 것입니다. Jenkins는 이런 output들을 보관하고 뭔가 이상이 있을 때 여러분이 알아채기 쉽도록 도와줍니다.




구글에서 Jenkins 로 검색하면 이런 미인 사진도 나옵니다.

하지만 지금 공부하고 있는 Jenkins의 로고는 아래 이미지 입니다.




Who is using it?

많은 회사들과 기관들이 이 Jenkins를 사용하고 있습니다. 대부분의 instance들은 firewall 안에서 실행되죠. 구글 같은 경우에는 public하게 visible한 instance들을 사용하기도 합니다. 여기를 보시면 anonymouse usage를 수집한 통계 자료를 보실 수도 있습니다.


Features

Jenkins 에는 아래와 같은 기능들이 있습니다.:

    Easy installation: Just java -jar jenkins.war, 혹은 a servlet container 내에 deploy. 다른 추가적인 인스톨 필요 없음. 데이타베이스 필요 없음.
    Easy configuration: web GUI를 통해서 쉽게 configuration을 할 수 있음. 실시간으로 에러 확인이 가능하고 inline help를 받을 수 있음. XML를 수정하고 하는 작업이 필요 없음. XML을 통해서 configuration을 하고 싶으면 그렇게 할 수도 있음
    Change set support: Jenkins는 Subversion/CVS로부터 빌드하면서 만들어진 변화들의 리스트를 generate 할 수 있음. 이것은 repository 의 load를 줄이기 위한 아주 효율적인 방법입니다.
    Permanent links: Jenkins는 대부분의 페이지에 대해 깔끔하고 가독성 있는 URL을 제공합니다. "latest build"/"latest successful build" 같은 permalinks등을 포함해서요. 그래서 다른 곳에서도 쉽게 링크 작업을 할 수 있습니다.
    RSS/E-mail/IM Integration: Monitor build results by RSS or e-mail to get real-time notifications on failures. failure에 대한 실시간 notification들을 얻기 위한 RSS나 이메일에 의한 빌드 결과 모니터 함.
    After-the-fact tagging: 오래전에 빌드가 완료된 것에 대해서도 tag가 가능합니다.
    JUnit/TestNG test reporting: JUnit test report를 보여 줍니다. history 정보도 보여주고요. history trend도 그래프로 보여줍니다.
    Distributed builds: Jenkins can distribute build/test loads to multiple computers. This lets you get the most out of those idle workstations sitting beneath developers' desks.
    File fingerprinting: Jenkins can keep track of which build produced which jars, and which build is using which version of jars, and so on. This works even for jars that are produced outside Jenkins, and is ideal for projects to track dependency.
    Plugin Support: Jenkins can be extended via 3rd party plugins. You can write plugins to make Jenkins support tools/processes that your team uses.



Jenkins Best Practices

최근 automated test를 진행하는 Continuous Integration에서 Jenkins를 많이 채택하고 있습니다. CI(Continuous Integration)의 개념이 이제는 Build Management, Release Management, Deployment Automation, and Test Orchestration 쪽으로 바뀌고 있습니다. 이 섹션에서는 Jenkins를 사용하기 위한 best practic 들의 세트를 제공합니다. - A Continuous Integration Solution to provide executives, business managers, software developers and architects a better sense of the development progress and code quality of projects throughout the development lifecycle. (View Jenkins Best Practices)


Introductory Articles

아래 링크들 중 많은 부분이 Hudson을 참조하고 있습니다. Jenkins의 원래 이름이 Hudson 이었습니다.



   

Test Drive

test drive를 위해 Java Web Start를 통해서 Jenkins를 시작합니다. jenkins가 시작되면 dashboard를 얻기 위해  http://localhost:8080/로 갑니다. 이 Jenkins에 여러분이 세팅한 configuration은 ~/.jenkins에 저장됩니다. Jenkins가 restart 하게 되면 이 저장된 configuration을 참조해서 계속 적용되게 됩니다.



Installation

Jenkins를 실행하려면 JRE 1.5 버전 이상이 깔려 있어야 합니다. 그 다음 jenkins.war를 다운 받은 후 java -jar jenkins.war 라는 명령어를 통해서 이것을 실행 시킬 수 있습니다. 이것은 기본적으로 test drive와 같은 set up 인데요 다만 oupput 이 윈도우가 아니라 콘솔로 가는 것이 다릅니다.

그리고 여러분이 Glassfish, Tomcat 5, JBoss, Jetty 6 같은 Servlet 2.4/JSP 2.0 이후 버전을 지원하는 서블릿 콘테이너를 가지고 있다면 jenkins.war를 다른 WAR 파일처럼 deploy 할 수 있습니다. container-specific installation instruction 에 대해 좀 더 자세히 할고 싶으시면 이 문서를 보세요.

일단 war 파일이 explod 되면 jenkins/WEB-INF directory 안에서 chmod 755 jenkins를 해 주세요. 이렇게 해야 이 shell script를 실행할 수 있습니다.

만약 Window에서 실행한다면 Jenkins를 service로서 실행해야 할 겁니다. 그래야 자동적으로 start up 시킬 수 있을테니까요. 이렇게 하기 위한 가장 쉬운 방법은 Jenkins를 native Windows installer를 다운받아 인스톨 하면 됩니다. 이 파일은 Jenkins main page에서 다운 받으실 수 있습니다. .zip 파일을 다운 받으신 후 압축을 풀고 인스톨 파일을 클릭하시기만 하면 설치 마법사를 통해 설치하실 수 있습니다. 이 설치 마법사가 Jenkins를 인스톨 하고 Jenkins Windows service도 셋업해 줄 겁니다.

Jenkins를 서비스로 등록할 수 있는 다른 방법은 톰캣을 서비스로서 인스톨하고 Jenkins를 이곳에 deploy 하는 방법도 있습니다. 그리고 Java Service Wrapper를 사용할 수도 있구요. 그런데 service wrapper를 사용하면 약간의 문제가 있을 수도 있습니다. 왜냐하면 디폴트 namespace에 있는 Jenkins의 Main 클래스가 이 service wrapper main class와 충돌할 수 있기 때문이죠. service container (Tomcat, Jetty, etc.) 안에 deploy 하는 것이 아마 좀 더 직관적일 겁니다. 이와 관련해 별로 경험이 없는 개발자들도 쉽게 하실 수 있습니다.

다양한 환경에서 다른 사람들은 Jenkins/Hudson을 어떻게 deploy 했는지 보시려면 아래 링크들을 참조하세요.



License

The license for the core might be found at https://github.com/jenkinsci/jenkins/blob/master/LICENSE.txt


반응형


반응형

지난 프로젝트에 이어 이번 프로젝트에서도 Automated Testing 일을 하게 됐습니다.


지난번에는 제대로 자리가 잡히지 않은 테스팅 팀이라서 좀 힘들었는데 이곳은 테스팅 팀만 40명이고 제대로 체계가 잡혀 있는 것 같더라구요.


요즘 KT (Knowledge Transfer) Session 을 갖고 있습니다.


모바일 앱 개발에 관심이 있지만 어떻게 하다 보니까 연달아 이렇게 테스팅 일을 하게 되네요.

흘러가는 물에 띄워진 나뭇잎처럼 그냥 흐름에 나를 맡기자는게 제 인생 철학이라서 닥치는대로 그냥 열심히 하고 있습니다.


이곳 미국으로 온 이유 중 하나가 한국에서 접하기 어려운 기술을 배우고 좀 더 글로벌한 조직 문화를 접해서 그 효율성을 한번 비교해 보고 싶은 것도 있었거든요.


테스팅은 한국에서는 거의 무시되다시피하는 분야인데 이렇게 직접 실무 현장에서 배울 수 있는 기회가 와서 무척 기쁩니다.


더군다나 지금 참여하고 있는 프로젝트는 (리더의 말에 의하면) 현재 Market에서는 볼 수 없는 이곳 만의 노력과 노하우로 만든 프레임워크라서 더더욱 새로운 것들을 배울 수 있는 기회인 것 같구요.


혹시 이곳의 테스팅 프레임워크가 나중에 시장의 Standard가 될 수도 있지 않을까 하는 기대가 있습니다.


KT Session 에서 현재 프로젝트에서 사용되는 여러가지 툴들은 어떤게 있는지 들었습니다.

경험이 있는 것도 있고 생소한 것들도 있습니다.



일단 그 리스트를 정리해 놓고 하나하나 배워 나가야 겠습니다.


주요 툴

- Rainbow Framework

- Jenkins

- TestNG

-Selenium WebDrive

- Maven


기타 툴

- Sikuli

- AutoIT3

- HP ALM Software





주요 툴들 중에는 TestNG, Selenium WebDrive, Maven 정도가 익숙한 툴입니다.

그리고 얘기 들어보니까 Jenkins 를 숙달되게 사용해야 되겠더라구요.

Jenkins 는 지난 프로젝트에서 다뤄본 적은 있지만 그냥 Testing 에 대한 Report 나 Build 결과를 보는 정도 였습니다.

이 Jenkins 에 대해서 자세히 공부를 해야 겠네요.


반응형

JUnit 4 와 TestNG 비교하기

2013. 11. 3. 23:51 | Posted by 솔웅


반응형

JUnit 4 Vs TestNG – Comparison


JUnit 4 와 TestNG는 자바에서 유닛 테스트 프레임워크로는 아주 유명합니다. 이 둘은 기능적인 면에서 아주 비슷합니다. 어느것이 더 좋을까요? 현재 참여중인 자바 프로젝트에서 어떤 프레임워크를 사용해야 할 까요?


아래 JUnit 4와 TestNG를 기능적으로 비교해 봤습니다.





1. Annotation Support

annotation 을 지원하는 것은 JUnit 4나 TestNG 모두 비슷하게 사용하고 있습니다.


FeatureJUnit 4TestNG
test annotation@Test@Test
run before all tests in this suite have run@BeforeSuite
run after all tests in this suite have run@AfterSuite
run before the test@BeforeTest
run after the test@AfterTest
run before the first test method that belongs to any of these groups is invoked@BeforeGroups
run after the last test method that belongs to any of these groups is invoked@AfterGroups
run before the first test method in the current class is invoked@BeforeClass@BeforeClass
run after all the test methods in the current class have been run@AfterClass@AfterClass
run before each test method@Before@BeforeMethod
run after each test method@After@AfterMethod
ignore test@ignore@Test(enbale=false)
expected exception@Test(expected = ArithmeticException.class)@Test(expectedExceptions = ArithmeticException.class)
timeout@Test(timeout = 1000)@Test(timeout = 1000)


JUnit 4와 TestNG 사이에서 사용하는 annotation의 차이점은 아래와 같습니다.


1. In JUnit 4, we have to declare “@BeforeClass” and “@AfterClass” method as static method. TestNG is more flexible in method declaration, it does not have this constraints.


2. 3 additional setUp/tearDown level: suite and group (@Before/AfterSuite, @Before/AfterTest, @Before/AfterGroup). See more detail here.


JUnit 4

    @BeforeClass
    public static void oneTimeSetUp() {
        // one-time initialization code   
    	System.out.println("@BeforeClass - oneTimeSetUp");
    }


TestNG

    @BeforeClass
    public void oneTimeSetUp() {
        // one-time initialization code   
    	System.out.println("@BeforeClass - oneTimeSetUp");
}



JUnit 4에서는 annotation 이름이 약간 불명확 합니다. Befor, After, Expected 는 정확히 무엇을 의미하는지 그 이름만으로 파악하기 힘듭니다. 반면에 TestNG는 BeforeMethod, AfterMethod, ExpectedException 을 사용해서 이름만 듣고 어떤 의미인지 파악이 가능합니다.



2. Exception Test


exception testing 이란 해당 단위테스트에서 throw될 예외가 어떤 것인지 설정해서 테스트해 볼 수 있다는 것을 말합니다. 이 기능은 JUnit 과 TestNG 모두에 있습니다.



JUnit 4

      @Test(expected = ArithmeticException.class)  
	public void divisionWithException() {  
	  int i = 1/0;
	}


TestNG

      @Test(expectedExceptions = ArithmeticException.class)  
	public void divisionWithException() {  
	  int i = 1/0;
	}


3. Ignore Test


Ignored 는 단위테스트를 실행하지 않도록 하는 겁니다. 이것도 두 프레임워크 모두에서 지원합니다.


JUnit 4

        @Ignore("Not Ready to Run")  
	@Test
	public void divisionWithException() {  
	  System.out.println("Method is not ready yet");
	}


TestNG

	@Test(enabled=false)
	public void divisionWithException() {  
	  System.out.println("Method is not ready yet");
	}


4. Time Test

Time Test는 단위 테스트를 시행하는 시간이 특정 시간 보다 더 걸리게 되면 테스트를 fail로 처리하도록 하는 기능입니다. 이것도 두 프레임워크 모두에서 지원합니다.


JUnit 4

        @Test(timeout = 1000)  
	public void infinity() {  
		while (true);  
	}



TestNG

	@Test(timeOut = 1000)  
	public void infinity() {  
		while (true);  
	}


5. Suite Test


Suite Test는 여러개의 단위 테스트를 묶어서 실행하는 것을 말합니다. 이 기능도 두 프레임워크 모두에서 사용하고 있습니다. 하지만 그 사용법은 많이 다릅니다.


JUnit 4


The “@RunWith” and “@Suite” are use to run the suite test. The below class means both unit test “JunitTest1” and “JunitTest2” run together after JunitTest5 executed. All the declaration is define inside the class.


@RunWith(Suite.class)
@Suite.SuiteClasses({
        JunitTest1.class,
        JunitTest2.class
})
public class JunitTest5 {
}


TestNG


TestNG에서는 XML 파일이 사용됩니다. 아래 XML 파일은 TestNGTest1과 TestNGTest2를 같이 테스트하기 위해 만든 겁니다.

<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite">
  <test name="testing">
    <classes>
       <class name="com.fsecure.demo.testng.TestNGTest1" />
       <class name="com.fsecure.demo.testng.TestNGTest2" />
    </classes>
  </test>
</suite>



TestNG는 bundle class testing 이외의 것에도 활용할 수 있습니다. 물론 단위테스트를 bundle로 실행하는데도 사용할 수 있구요. TestNG에는 Grouping이라는 개념이 있습니다. 각 메소드들은 그룹에 속해 있습니다. 이렇게 함으로서 기능별로 테스트들을 카테고리로 만들 수 있습니다.

예를 들어 아래 예제를 보시면 3개의 그룹에 4개의 메소드를 포함하고 있는 한개의 클래스를 사용하는 방법을 보여 줍니다.

        @Test(groups="method1")
	public void testingMethod1() {  
	  System.out.println("Method - testingMethod1()");
	}  
 
	@Test(groups="method2")
	public void testingMethod2() {  
		System.out.println("Method - testingMethod2()");
	}  
 
	@Test(groups="method1")
	public void testingMethod1_1() {  
		System.out.println("Method - testingMethod1_1()");
	}  
 
	@Test(groups="method4")
	public void testingMethod4() {  
		System.out.println("Method - testingMethod4()");
	}


With the following XML file, we can execute the unit test with group “method1” only.

<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite">
  <test name="testing">
  	<groups>
      <run>
        <include name="method1"/>
      </run>
    </groups>
    <classes>
       <class name="com.fsecure.demo.testng.TestNGTest5_2_0" />
    </classes>
  </test>
</suite>


Grouping이라는 개념을 사용하면 integration test도 가능하도록 합니다. 예를 들어 단위 테스트 클래스들 중 DatabaseFunction 그룹만 따로 테스트하는 일 등이 가능합니다.


6. Parameterized Test


Parameterized Test는 단위 테스트에 파라미터 값을 사용한다는 것입니다. 이 기능두 두 프레임워크 모두 사용하고 있습니다. 하지만 그 사용법은 많이 다릅니다.


JUnit 4


The “@RunWith” and “@Parameter” is use to provide parameter value for unit test, @Parameters have to return List[], and the parameter will pass into class constructor as argument.


@RunWith(value = Parameterized.class)
public class JunitTest6 {
 
	 private int number;
 
	 public JunitTest6(int number) {
	    this.number = number;
	 }
 
	 @Parameters
	 public static Collection<Object[]> data() {
	   Object[][] data = new Object[][] { { 1 }, { 2 }, { 3 }, { 4 } };
	   return Arrays.asList(data);
	 }
 
	 @Test
	 public void pushTest() {
	   System.out.println("Parameterized Number is : " + number);
	 }
}


It has many limitations here; we have to follow the “JUnit” way to declare the parameter, and the parameter has to pass into constructor in order to initialize the class member as parameter value for testing. The return type of parameter class is “List []”, data has been limited to String or a primitive value for testing.



TestNG


XML 파일을 사용하거나 @DataProvider 를 사용해서 parameterized test를 수행할 수 있습니다.


XML file for parameterized test.


Only “@Parameters” declares in method which needs parameter for testing, the parametric data will provide in TestNG’s XML configuration files. By doing this, we can reuse a single test case with different data sets and even get different results. In addition, even end user, QA or QE can provide their own data in XML file for testing.


Unit Test

      public class TestNGTest6_1_0 {
 
	   @Test
	   @Parameters(value="number")
	   public void parameterIntTest(int number) {
	      System.out.println("Parameterized Number is : " + number);
	   }
 
      }


XML File

<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite">
  <test name="testing">
 
    <parameter name="number" value="2"/> 	
 
    <classes>
       <class name="com.fsecure.demo.testng.TestNGTest6_0" />
    </classes>
  </test>
</suite>


@DataProvider for parameterized test.


While pulling data values into an XML file can be quite handy, tests occasionally require complex types, which can’t be represented as a String or a primitive value. TestNG handles this scenario with its @DataProvider annotation, which facilitates the mapping of complex parameter types to a test method.

@DataProvider for Vector, String or Integer as parameter

        @Test(dataProvider = "Data-Provider-Function")
	public void parameterIntTest(Class clzz, String[] number) {
	   System.out.println("Parameterized Number is : " + number[0]);
	   System.out.println("Parameterized Number is : " + number[1]);
	}
 
	//This function will provide the patameter data
	@DataProvider(name = "Data-Provider-Function")
	public Object[][] parameterIntTestProvider() {
		return new Object[][]{
				   {Vector.class, new String[] {"java.util.AbstractList", 
"java.util.AbstractCollection"}},
				   {String.class, new String[] {"1", "2"}},
				   {Integer.class, new String[] {"1", "2"}}
				  };
	}


@DataProvider for object as parameter


P.S “TestNGTest6_3_0” is an simple object with just get set method for demo.

        @Test(dataProvider = "Data-Provider-Function")
	public void parameterIntTest(TestNGTest6_3_0 clzz) {
	   System.out.println("Parameterized Number is : " + clzz.getMsg());
	   System.out.println("Parameterized Number is : " + clzz.getNumber());
	}
 
	//This function will provide the patameter data
	@DataProvider(name = "Data-Provider-Function")
	public Object[][] parameterIntTestProvider() {
 
		TestNGTest6_3_0 obj = new TestNGTest6_3_0();
		obj.setMsg("Hello");
		obj.setNumber(123);
 
		return new Object[][]{
				   {obj}
		};
	}



TestNG의 parameterized test는 사용자 편의적이고 아주 유연합니다. (XML 파일을 사용할 수도 있고 클래스 내에서 선언할 수도 있습니다.) 또한 파라미터로서 여러 데이터 타입을 사용할 수 있습니다. 위 예제에서 볼 수 있듯이 우리가 만든 object(TestNGTest6_3_0)도 pass 할 수 있습니다.


7. Dependency Test


The “Parameterized Test” means methods are test base on dependency, which will execute before a desired method. If the dependent method fails, then all subsequent tests will be skipped, not marked as failed.


JUnit 4


JUnit 프레임워크는 단위 테스트에만 촛점이 맞춰져 있습니다. 이 Dependency Test에 대해서는 아직 기능을 제공하지 않고 있습니다.


TestNG


TestNG에서는 dependency testiong을 사용하기 위해 dependOnMethods를 사용합니다.

        @Test
	public void method1() {
	   System.out.println("This is method 1");
	}
 
	@Test(dependsOnMethods={"method1"})
	public void method2() {
		System.out.println("This is method 2");
	}



method2() 는 method1()이 성공했을 때만 실행됩니다. 


Conclusion

이 두 프레임워크의 기능을 모두 비교해 보고 난 후 저는 자바 프로젝트에 사용할 core unit test framework로 TestNG를 사용할 것을 권장합니다. 왜냐하면 TestNG는 좀 더 발전된 parameterized testing, dependency testing 그리고 suite testing (Grouping concept)를 지원하기 때문입니다. TestNG is meant for high-level testing and complex integration test.

TestNG의 유연성은 더 규모가 큰 test suites를 효과적으로 수행할 수 있도록 도와 줍니다. 또한 TestNG는 core JUnit 4 기능을 모두 cover 합니다. 저는 더 이상 JUnit을 사용할 이유를 찾을 수가 없네요.

References

TestNG
————
http://en.wikipedia.org/wiki/TestNG
http://www.ibm.com/developerworks/java/library/j-testng/
http://testng.org/doc/index.html
http://beust.com/weblog/

JUnit
———–
http://en.wikipedia.org/wiki/JUnit
http://www.ibm.com/developerworks/java/library/j-junit4.html
http://junit.sourceforge.net/doc/faq/faq.htm
http://www.devx.com/Java/Article/31983/0/page/3
http://ourcraft.wordpress.com/2008/08/27/writing-a-parameterized-junit-test/

TestNG VS JUnit
——————
http://docs.codehaus.org/display/XPR/Migration+to+JUnit4+or+TestNG
http://www.ibm.com/developerworks/java/library/j-cq08296/index.html
http://www.cavdar.net/2008/07/21/junit-4-in-60-seconds/



반응형

TestNG Tutorial 정리 02

2013. 11. 1. 03:49 | Posted by 솔웅


반응형

지난 글에 이어서 http://www.mkyong.com/tutorials/testng-tutorials/ 에 잘 정리된 TestNG 튜토리얼을 제 블로그에 다시 정리 합니다.


TestNG Tutorial 6 – Parameterized Test


The “Parameterized Test” means vary parameter value for unit test. In TestNG, XML file or “@DataProvider” is used to provide vary parameter for unit testing.


TestNG에서는 XML 파일이나 @DataProvider 를 사용해서 파라미터를 전달하네요.

지난번 Selenium에 대한 글에서 이 부분도 있었던 것 같습니다.

jUnit 에서 Mockito를 사용해서 객체를 mocking하고 inject 를 사용해서 데이터를 세팅하는 것과 비슷한 일을 한다는 인상을 받았는데 이 예제를 보고 확실하게 그런지 어떤지 공부해 봐야겠습니다.


1. XML file for parameterized test.

Declare “@Parameters” annotation in method which needs parameter testing, the parametric data will be provide by TestNG’s XML configuration files. By doing this, you can reuse a single test case with different data sets easily. In addition, even end user, QA or QE can provide their own data sets for testing.


import org.testng.annotations.*;
 
/**
 * TestNG Parameterized Test
 * @author mkyong
 *
 */
public class TestNGTest6_1_0 {
 
	@Test
	@Parameters(value="number")
	public void parameterIntTest(int number) {
	   System.out.println("Parameterized Number is : " + number);
	}
}

<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite"> <test name="testing">   <parameter name="number" value="2"/>   <classes> <class name="TestNGTest6_1_0" /> </classes> </test> </suite>


Result

Parameterized Number is : 2

jUnit 에서 봤던 mocking and injecting 이랑은 좀 다르군요.

TestNG에서는 해당 테스트 메소드에 직접 데이터를 파라미터로 전달하는 방법이 있네요.

jUnit을 사용할 때 이런 방법을 사용한적은 없는것 같은데....

TestNG에만 있는 기능인지 아니면 제가 field에서 jUnit의 이런 기능을 사용하지 않았던가 하겠네요.


2. @DataProvider for parameterized test.

While pulling data values into an XML file can be quite handy, but test cases occasionally may require complex data types, which can’t be represented as a String or a primitive value in XML file. TestNG handles this scenario with @DataProvider annotation, which facilitates the mapping of complex parameter types to a test method.

1. @DataProvider for Vector, String or Integer as parameter
import java.util.Vector;
import org.testng.annotations.*;
 
/**
 * TestNG Parameterized Test - Advance
 * @author mkyong
 *
 */
public class TestNGTest6_2 {
 
 
	@Test(dataProvider = "Data-Provider-Function")
	public void parameterIntTest(Class clzz, String[] number) {
	   System.out.println("Parameterized Number is : " + number[0]);
	   System.out.println("Parameterized Number is : " + number[1]);
	}
 
	//This function will provide the parameter data
	@DataProvider(name = "Data-Provider-Function")
	public Object[][] parameterIntTestProvider() {
		return new Object[][]{
		   {Vector.class, new String[] {"java.util.AbstractList", 
                                                 "java.util.AbstractCollection"}},
		   {String.class, new String[] {"1", "2"}},
		   {Integer.class, new String[] {"1", "2"}}
	       };
	}
}


Result

Parameterized Number is : java.util.AbstractList
Parameterized Number is : java.util.AbstractCollection
Parameterized Number is : 1
Parameterized Number is : 2
Parameterized Number is : 1
Parameterized Number is : 2
PASSED: parameterIntTest(class java.util.Vector, [Ljava.lang.String;@1016632)
PASSED: parameterIntTest(class java.lang.String, [Ljava.lang.String;@10a6ae2)
PASSED: parameterIntTest(class java.lang.Integer, [Ljava.lang.String;@4a6cbf)

2. @DataProvider for object as parameter

“TestNGTest6_3_0” is a simple object with simple get set methods.

/**
 * TestNG Parameterized Test - Advance
 * @author mkyong
 *
 */
public class TestNGTest6_3_0 {
 
	private int number;
	private String msg;
 
	public void setNumber(int number){
		this.number = number;
	}
 
	public int getNumber(){
		return this.number;
	}
 
	public void setMsg(String msg){
		this.msg = msg;
	}
 
	public String getMsg(){
		return this.msg;
	}
 
}

import
org.testng.annotations.*;   /** * TestNG Parameterized Test - Advance * @author mkyong * */ public class TestNGTest6_3_1 {   @Test(dataProvider = "Data-Provider-Function") public void parameterIntTest(TestNGTest6_3_0 clzz) { System.out.println("Parameterized Number is : " + clzz.getMsg()); System.out.println("Parameterized Number is : " + clzz.getNumber()); }   //This function will provide the patameter data @DataProvider(name = "Data-Provider-Function") public Object[][] parameterIntTestProvider() {   TestNGTest6_3_0 obj = new TestNGTest6_3_0(); obj.setMsg("Hello"); obj.setNumber(123);   return new Object[][]{ {obj} }; } }


Result

Parameterized Number is : 123
PASSED: parameterIntTest(TestNGTest6_3_0@dc6a77)


TestNG’s Parameterized test is very user friendly and flexible (either in XML file or inside the class). It can support many complex data type as parameter value and the possibility is unlimited. As example above, you even can pass in your own object (TestNGTest6_3_0) for Parameterized test.


이걸 이용하면 테스트 케이스를 작성해 놓고 유저가 원하는 데이터를 보내서 테스트를 해 볼 수 있겠습니다.

그런데 이 기능은 FitNesse 를 사용하면 훨씬 더 좋을 것 같군요.



TestNG parameter testing example


TestNG parameter testing example.


Problem

Let’s say, a utility class has a function for converting the character to ASCII or vice verse, how can you test it with TestNG?


Solution

You can create a unit test function which accept two parameters (character and expected ASCII) from TestNG data provider, and assert the value like following :



Example in Java

package com.mkyong.common;
/**
 * Character Utility class
 * @author mkyong
 *
 */
public class CharUtils 
{   
	/**
	 * Convert the characters to ASCII value
	 * @param character character
	 * @return ASCII value
	 */
	public static int CharToASCII(final char character){
		return (int)character;
	}
 
	/**
	 * Convert the ASCII value to character
	 * @param ascii ascii value
	 * @return character value
	 */
	public static char ASCIIToChar(final int ascii){
		return (char)ascii;		
	}
}



Unit Test

package com.mkyong.common;
import org.testng.Assert;
import org.testng.annotations.*;
/**
 * Character Utils Testing
 * @author mkyong
 *
 */
public class CharUtilsTest {
 
	@DataProvider
	public Object[][] ValidDataProvider() {
		return new Object[][]{
				{ 'A', 65 },{ 'a', 97 },
				{ 'B', 66 },{ 'b', 98 },
				{ 'C', 67 },{ 'c', 99 },
				{ 'D', 68 },{ 'd', 100 },
				{ 'Z', 90 },{ 'z', 122 },
				{ '1', 49 },{ '9', 57 },
 
		};
	}
 
	@Test(dataProvider = "ValidDataProvider")
	public void CharToASCIITest(final char character, final int ascii) {
		   int result = CharUtils.CharToASCII(character); 
		   Assert.assertEquals(result, ascii);
	}
 
	@Test(dataProvider = "ValidDataProvider")
	public void ASCIIToCharTest(final char character, final int ascii) {
		   char result = CharUtils.ASCIIToChar(ascii); 
		   Assert.assertEquals(result, character); 
	}
}


Result

PASSED: CharToASCIITest(A, 65)
PASSED: CharToASCIITest(a, 97)
PASSED: CharToASCIITest(B, 66)
PASSED: CharToASCIITest(b, 98)
PASSED: CharToASCIITest(C, 67)
PASSED: CharToASCIITest(c, 99)
PASSED: CharToASCIITest(D, 68)
PASSED: CharToASCIITest(d, 100)
PASSED: CharToASCIITest(Z, 90)
PASSED: CharToASCIITest(z, 122)
PASSED: CharToASCIITest(1, 49)
PASSED: CharToASCIITest(9, 57)
PASSED: ASCIIToCharTest(A, 65)
PASSED: ASCIIToCharTest(a, 97)
PASSED: ASCIIToCharTest(B, 66)
PASSED: ASCIIToCharTest(b, 98)
PASSED: ASCIIToCharTest(C, 67)
PASSED: ASCIIToCharTest(c, 99)
PASSED: ASCIIToCharTest(D, 68)
PASSED: ASCIIToCharTest(d, 100)
PASSED: ASCIIToCharTest(Z, 90)
PASSED: ASCIIToCharTest(z, 122)
PASSED: ASCIIToCharTest(1, 49)
PASSED: ASCIIToCharTest(9, 57)
 
===============================================
    com.mkyong.common.CharUtilsTest
    Tests run: 24, Failures: 0, Skips: 0
===============================================
 
 
===============================================
mkyong
Total tests run: 24, Failures: 0, Skips: 0
===============================================




TestNG Tutorial 7 – Dependency Test


The “Dependency Test” means methods are test base on dependency. If the dependent method fails, all the subsequent test methods will be skipped, not marked as failed.

TestNG uses “dependOnMethods“ to implement the dependency testing as following


이 기능은 어떤 특정 테스트가 실행되고 난 다음에 다른 메소드를 실행하고 싶을 때 사용되는 기능입니다.



import
org.testng.annotations.*;   /** * TestNG Dependency Test * @author mkyong * */ public class TestNGTest7 {   @Test public void method1() { System.out.println("This is method 1"); }   @Test(dependsOnMethods={"method1"}) public void method2() { System.out.println("This is method 2"); } }


Result

PASSED: method1
PASSED: method2


The “method2()” will execute only if “method1()” is run successfully, else “method2()” will skip.



튜토리얼을 쭉 봤는데요.

데이터를 Mocking 해야 될 때 TestNG는 어떻게 하는지 잘 모르겠네요.

jUnit 에서는 Mockito를 사용했는데 TestNG에서도 그런지....

다음에는 이 방법을 좀 알아봐야 겠습니다.

반응형

TestNG Tutorial 정리 01

2013. 11. 1. 03:32 | Posted by 솔웅


반응형

Selenium 과 같이 TestNG가 많이 사용되는 것 같아 TestNG에 대해서도 정리를 해 두기로 했습니다.

지난 프로젝트에서 jUnit을 사용해서 비슷한 개념의 TestNG를 배우는게 그렇게 부담이 되지는 않아 다행이네요.

TestNG만의 특징 및 장점을 파악해 둬서 나중에 실무에서 유용하게 사용해야 되겠습니다.


아래 튜토리얼은 구글링 하다가 http://www.mkyong.com/tutorials/testng-tutorials/ 에서 찾은 글들 입니다.



TestNG Tutorial 1 – Basic usage


import java.util.*;
import org.testng.Assert;
import org.testng.annotations.*;
 
public class TestNGTest1 {
 
    private Collection collection;
 
    @BeforeClass
    public void oneTimeSetUp() {
        // one-time initialization code   
    	System.out.println("@BeforeClass - oneTimeSetUp");
    }
 
    @AfterClass
    public void oneTimeTearDown() {
        // one-time cleanup code
    	System.out.println("@AfterClass - oneTimeTearDown");
    }
 
    @BeforeMethod
    public void setUp() {
        collection = new ArrayList();
        System.out.println("@BeforeMethod - setUp");
    }
 
    @AfterMethod
    public void tearDown() {
        collection.clear();
        System.out.println("@AfterMethod - tearDown");
    }
 
    @Test
    public void testEmptyCollection() {
        Assert.assertEquals(collection.isEmpty(),true);
        System.out.println("@Test - testEmptyCollection");
    }
 
    @Test
    public void testOneItemCollection() {
        collection.add("itemA");
        Assert.assertEquals(collection.size(),1);
        System.out.println("@Test - testOneItemCollection");
    }
}


Result

@BeforeClass - oneTimeSetUp
@BeforeMethod - setUp
@Test - testEmptyCollection
@AfterMethod - tearDown
@BeforeMethod - setUp
@Test - testOneItemCollection
@AfterMethod - tearDown
@AfterClass - oneTimeTearDown
PASSED: testEmptyCollection
PASSED: testOneItemCollection



TestNG Tutorial 2 – Expected Exception Test

import org.testng.annotations.*;
 
/**
 * TestNG Expected Exception Test
 * @author mkyong
 *
 */
public class TestNGTest2 {
 
	@Test(expectedExceptions = ArithmeticException.class)  
	public void divisionWithException() {  
	  int i = 1/0;
	}  
 
}



In above example, the divisionWithException() method will throw an ArithmeticException Exception, since this is an expected exception, so the unit test will pass.


위 예제에서는 annotation으로 ArithmeticException.class 가 예상되는 메소드라고 선언해 줬습니다.

jUnit이랑 Exception 테스트하는 부분은 비슷하네요.

divisionWithException() method가 ArithmeticException Exception을 throw 할 테니까 이 테스트는 pass 되게 됩니다.



TestNG Tutorial 3 – Ignore Test


This “Ignored” means the method is not ready to test, the TestNG engine will just bypass this method.


완료하지 않은 테스트 메소드의 실행을 하고 싶지 않을 때 TestNG는 아래와 같이 enabled=false annotation을 사용합니다.

jUnit에서는 ignore 를 사용햇던 것 같은데 약간 사용법이 다르군요.


import org.testng.annotations.*;
 
/**
 * TestNG Ignore Test
 * @author mkyong
 *
 */
public class TestNGTest3 {
 
	@Test(enabled=false)
	public void divisionWithException() {  
	  System.out.println("Method is not ready yet");
	}  
 
}

In above example, TestNG will not test the divisionWithException() method.



TestNG Tutorial 4 – Time Test

The “Time Test” means if an unit test takes longer than the specified number of milliseconds to run, the test will terminated and mark as failed.


이 테스트를 실행하는데 일정 시간 이상 지나면 그냥 fail로 처리하고 싶을 때 TestNG에서는 이 timeout annotation을 사용합니다.


import org.testng.annotations.*;
 
/**
 * TestNG TimeOut Test
 * @author mkyong
 *
 */
public class TestNGTest4 {
 
	@Test(timeOut = 1000)  
	public void infinity() {  
		while (true);  
	}  
 
}


In above example, the infinity() method will not return, so the TestNG engine will mark it as failed and throw an exception


FAILED: infinity
org.testng.internal.thread.ThreadTimeoutException: 
Method public void TestNGTest4.infinity() didn't finish within the time-out 1000
... Removed 18 stack frames




TestNG Tutorial 5 – Suite Test


The “Suite Test” means bundle a few unit test cases and run it together.

In TestNG, XML file is use to define the suite test. The below XML file means both unit test “TestNGTest1” and “TestNGTest2” will execute together.


이 Suite Test는 Selenium을 다루는 글에서도 한번 다룬 적이 있습니다.

두 개 이상의 테스트 클래스를 테스트하고 싶을 때 사용합니다.


<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite">
  <test name="testing">
    <classes>
       <class name="TestNGTest1" />
       <class name="TestNGTest2" />
    </classes>
  </test>
</suite>


Beside classes bundle testing, TestNG provides a “Grouping” feature to bundle few methods as a single unit for testing, where every method is tie to a group.

For example, Here’s a class with four methods, three groups (method1, method2 and method3)


import org.testng.annotations.*;
 
/**
 * TestNG Grouping
 * @author mkyong
 *
 */
public class TestNGTest5_2_0 {
 
	@Test(groups="method1")
	public void testingMethod1() {  
	  System.out.println("Method - testingMethod1()");
	}  
 
	@Test(groups="method2")
	public void testingMethod2() {  
		System.out.println("Method - testingMethod2()");
	}  
 
	@Test(groups="method1")
	public void testingMethod1_1() {  
		System.out.println("Method - testingMethod1_1()");
	}  
 
	@Test(groups="method4")
	public void testingMethod4() {  
		System.out.println("Method - testingMethod4()");
	}    
}


You can execute the unit test with group “method1” only.


<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite">
  <test name="testing">
  	<groups>
      <run>
        <include name="method1"/>
      </run>
    </groups>
    <classes>
       <class name="TestNGTest5_2_0" />
    </classes>
  </test>
</suite>


Result

Method - testingMethod1_1()
Method - testingMethod1()


TestNG Grouping is highly flexible and useful, especially when you implement it in your project integration testing.





반응형


반응형

PageBase.java

GoogleSearchPage.java

GoogleSearchResultPage.java

GoogleSearchSuggestPage.java

GoogleSearch_withPageObject.java


지난 글에서 GoogleSearch_withPageObject 에 있는 testGoogleSearch() 를 분석했습니다.

이 메소드에서 사용하는 GoogleSearchPage와 GoogleSearchResultPage 클래스도 알아봤고 그 이전에 PageBase 클래스도 공부해 봤습니다.


오늘은 GoogleSearch_withPageObject 클래스에 있는 testGoogleSuggest() 메소드를 분석해 보겠습니다.



우선 이 메소드 안에서 사용되는 GoogleSearchSuggestPage 클래스를 보겠습니다.


public class GoogleSearchSuggestPage extends PageBase{
        private final static String pageTitle = "Google";

        private final static String SEARCH_FIELD_NAME = "q";
       
        /** Constructor */
        public GoogleSearchSuggestPage(WebDriver driver){
                super(driver, pageTitle);

                // set the default URL
                URL = "http://www.google.com/webhp?complete=1&hl=en";
        }

        /** Enter the Search Text in the Search form field */
        public void enterSearchForm(String searchText){
                driver.findElement(By.name(SEARCH_FIELD_NAME)).sendKeys(searchText);                
        }
       
        /** Submit the form and return the next page object.
         * Seperate function should be in a seperate method. */
        public GoogleSearchResultPage submitForm(){
                driver.findElement(By.name(SEARCH_FIELD_NAME)).submit();
                return new GoogleSearchResultPage(driver);
        }

        /**
         * Enter the query string, and get the list of the suggestions
         * It use WaitTool to wait for the div (class=gsq_a).
         *
         * @param searchText the query string to search for
         * @return the list of the google search suggestions
         */
        public List<WebElement> getSearchSuggestions(String searchText){
        // Enter the query string "Cheese"
                driver.findElement(By.name(SEARCH_FIELD_NAME)).sendKeys(searchText);

        // Wait and get the list of suggestions
        List<WebElement> allSuggestions = WaitTool.waitForListElementsPresent(driver, By.className("gsq_a"), 5);
                return allSuggestions;
        }
}


이 클래스도 PageBase 클래스를 extends 합니다.

PageBase는 이전 글에서 공부해 봤구요.

이 클래스에서도 GoogleSearchPage 클래스와 마찬가지로 pageTitle과 SEARCH_FIELD_NAME 이라는 String 객체들을 final 로 선언했습니다.


다음에 나오는 생성자에서도 super() 메소드를 사용해서 PageBase의 생성자를 실행하고 여기에 파라미터로 driver와 pageTitle을 전달합니다.


driver는 GoogleSearch_withPageObject 클래스의 testGoogleSuggest() 메소드에서 전달된 정보를 PageBase의 생성자에 전달할 겁니다.


이전글에서 다뤘던 testGoogleSuggest() 메소드에서는 HTMLUnitDriver를 전달했는데 이 testGoogleSuggest()에서는 어떤 정보를 전달하는지 조금 있다가 보겠습니다.


이 클래스의 생성자에서도 URL 을 정의했는데요. 이전에 다뤘던 GoogleSearchPage 클래스에서 정의했던것과 조금 다릅니다. 


이렇게 사용하는 driver가 다르고 URL 이 다를 경우 이렇게 Page Objects 디자인을 사용하면 메소드들을 재사용할 수 있어서 편리하고 유지관리도 편해 집니다.


다음 enterSearchForm() 메소드를 보면 By.name 으로 Element를 찾고 그것을 submit() 합니다.


그리고 submitForm() 메소드를 보면 GoogleSearchResultPage 를 return 하네요.

By.name으로 Element를 찾고 이것을 submit() 하고 이 GoogleSearchResultPage를 return 합니다.

이것은 GoogleSearchPage에서 사용했던 메소드를 재사용하는 겁니다. 그 다음에 getSearchSuggestion() 메소드를 보면 By.name으로 Element를 찾고 그 Element에 searchTest를 input 합니다.

그리고 나서 WaitTool 클래스에 있는 메소드 를 사용해서 Google Textbox에 표시되는 Autocomplete 리스트를 받아서 List 객체에 담습니다.

(이 WaitTool 클래스 분석은 생략하겠습니다.)


이제 GoogleSearch_withPageObject 클래스에서 아직 살펴보지 않았던 testGoogleSuggest() 메소드를 분석해 보겠습니다.


public static void testGoogleSuggest() throws Exception {
               
        GoogleSearchSuggestPage googleSuggestPage = new GoogleSearchSuggestPage(new FirefoxDriver());
       
        // Go to the Google Suggest home page: "http://www.google.com/webhp?complete=1&hl=en"
        googleSuggestPage.open();
       
        // Enter the query string "Cheese", and get the list the suggestions
        List<WebElement> allSuggestions = googleSuggestPage.getSearchSuggestions("Cheese");
       
        for (WebElement suggestion : allSuggestions) {
            System.out.println(suggestion.getText());
        }
     }


Page Objects를 사용하지 않은 클래스와 비교하면 무척 간단합니다.

그 이유는 여러 능을 GoogleSearchSuggestPage, GoogleSearchResultPage 그리고 PageBase 클래스 등에 분산해서 코딩을 했기 때문입니다.

driver를 생성하는 부분은 PageBase 클래스에 코딩해 뒀고 URL 세팅하는 기능 등은 GoogleSearchSuggestPage 클래스 코딩해 두는 등 기능별로 코드를 분산해서 여러 클래스에서 가져다 쓸 수 있도록 만들었습니다.


이게 Page Objects 디자인의 주요 특징입니다.


이 메소드를 보시면 GoogleSearchSuggestPage 에 FirefoxDriver를 전달하네요.

그러면 GoogleSearchSuggestPage 클래스는 이것을 받아서 자신이 만든 pageTitle과 함께 PageBase로 전달하고 PageBase에서는 FirefoxDriver 객체를 생성하게 됩니다.

그리고 open() 메소드로 해당 URL을 Firefox브라우저에 띄우고요.

그리고 allSuggestions 라는 리스트 객체에 구글 텍스트박스에 나오는 Autocomplete 리스트를 담습니다.


맨 마지막에 나오는 for 문에서는 이 리스트를 출력하는 거구요.


이렇게 해서 Page Objects를 사용하는 샘플을 모두 분석해 봤네요.


이제 대충 Selenium 에서 Page Objects를 어떻게 사용하는지 알것 같습니다.








반응형


반응형

PageBase.java

GoogleSearchPage.java

GoogleSearchResultPage.java

GoogleSearchSuggestPage.java

GoogleSearch_withPageObject.java



지난 글에서는 위 클래스 중 PageBase.java 코드를 분석했습니다.

이번에는 GoogleSearch_withPageObject 안에 있는 testGoogleSearch() 메소드에서 사용하는 클래스들(GoogleSearchPage,GoogleSearchResultPage)을 공부해 보겠습니다.



public class GoogleSearchPage extends PageBase{
       
        private final static String pageTitle = "Google";
       
        private final static String SEARCH_FIELD_NAME = "q";

        /** Constructor */
        public GoogleSearchPage(WebDriver driver){
                super(driver, pageTitle);

                // set the default URL
                URL = "http://www.google.com";
        }
       
        /** Returns the default URL */
        public String getURL(){
                return URL;
        }
       
        /** Enter the Search Text in the Search form field */
        public void enterSearchForm(String searchText){
                driver.findElement(By.name(SEARCH_FIELD_NAME)).sendKeys(searchText);                
        }
       
        /** Submit the form and return the next page object.
         * Seperate function should be in a seperate method. */
        public GoogleSearchResultPage submitForm(){
                driver.findElement(By.name(SEARCH_FIELD_NAME)).submit();
                return new GoogleSearchResultPage(driver);
        }
}


이 클래스를 보면 PageBase 를 extends 한것을 보실 수 있습니다.

이 PageBase 클래스를 상속함으로서 PageBase 클래스 안에 정의된 여러 기능들을 사용할 수 있습니다.

PageBase 클래스는 지난 글에서 분석했습니다.


GoogleSearchPage 클래스 안을 보면 우선 pageTitle이라는 String 객체를 초기화 하고 여기에 Google 글자를 할당했습니다.

그리고 SEARCH_FIELD_NAME 객체를 초기화 해서 q 라는 텍스트를 초기화 하구요.

이 두 String은 private로 선언해서 이 클래스 내부에서만 사용할 수 있고 static을 선언해서 클래스 내의 여러 인스턴스들이 공유할 수 있는 변수로 사용하겠다는 겁니다. 그리고 final 은 상수처럼 이 값을 나중에 바꾸지 못하게 하는 겁니다.


다음에 보니까 GoogleSearchPage() 메소드가 있네요. 이 메소드는 WebDriver를 파라미터로 전달 받습니다.

그리고 super()를 사용해서 PageBase 의 생성자를 실행시킵니다. 파라미터로는 WebDriver와 pageTitle을 전달하구요.

그리고 URL이라는 변수에 http://www.google.com 를 할당합니다.

이 URL은 PageBase에서 proteted로 선언된 String 입니다. GoogleSearchPage는 PageBase를 상속했기 때문에 이 변수를 사용할 수 있는 겁니다.


그 다음은 getURL() 메소드가 있고 이 메소드는 URL을 return 합니다.

그리고 enterSearchForm() 메소드가 String을 파라미터로 받습니다.

그리고 이 메소드에서 하는 일은 By.name으로 해당 Element를 찾고 즉 name이 q 인 Element를 찾고 여기에 전달받은 파라미터를 input 합니다.


다음 submitForm()은 전달받는 파라미터는 없지만 GoogleSearchResultPage 클래스를 return 하는 메소드 입니다.

여기서 하는일은 By.name으로 Element를 찾아서 그것을 submit 하도록 합니다.

그리고 GoogleSearchResultPage()를 실행해서 그 결과를 return 합니다.


자연스럽게 여기서 GoogleSearchResultPage 클래스를 보게 됐네요.


public class GoogleSearchResultPage extends PageBase{
       
        private static final String resultStats_id = "resultStats";
        private final static String pageTitle = "Google Search";
       
       
        public GoogleSearchResultPage(WebDriver driver){
                super(driver, pageTitle);        
        }
        
        /** Returns the search ResultStat. */
        public String getSearchResultStat(){
                return driver.findElement(By.id(resultStats_id)).getText();
        }  
}


이 클래스도 PageBase를 상속 받았습니다.

그리고 resultStats_id라는 String 변수에 resultStats를 할당하고 pageTitle 이라는 String 변수에는 Google Search를 할당합니다. 모두 final로 선언해서 이 값을 바꾸지 못하도록 합니다.


이어서 나오는 생성자는 super()를 사용해서 PageBase의 생성자를 실행시킵니다.


그리고 getSearchResultStat() 메소드에서는 By.id로 resultStats_id 값을 가지고 있는 id를 찾아서 거기에 있는 텍스트를 get 하고 그 결과를 return 합니다.


이제 PageBase 와 GoogleSearchPage 그리고 GoogleSearchResultPage를 모두 분석했습니다.


그러면 이제 GoogleSearch_withPageObject 의 testGoogleSearch() 메소드를 살펴 볼 수 있습니다.


public static void testGoogleSearch(){
            //Create a new instance of GoogleSearch page
            GoogleSearchPage googleSearchPage = new GoogleSearchPage(new HtmlUnitDriver());

        // go to Google ("http://www.google.com")
            googleSearchPage.open();

        // Enter something to search for
            googleSearchPage.enterSearchForm("Cheese!");

        // Now submit the form, and get the next page object
            GoogleSearchResultPage googleSearchResultPage = googleSearchPage.submitForm();
              

        // Verify: Check the title of the page        
        String pageTitle = googleSearchResultPage.getTitle();
        System.out.println("Page title is: " + pageTitle);
        assertTrue("Got title: " + pageTitle, pageTitle.contains("Cheese!"));
    }


처음에 new GoogleSearchPage()를 생성하는데 여기에 파라미터로 HtmlUnitDriver() 를 보내죠?

그러면 GoogleSearchPage 클래스의 생성자를 봐야 되는데요. 

저 위에서 봤듯이 이 생성자에서는 super() 를 사용해서 PageBase의 생성자를 호출합니다.

파라미터로는 WebDriver를 보내는데 이것은 testGoogleSearch() 메소드에서 HtmlUnitDriver()를 보냈습니다. 그리고 다른 파라미터는 GoogleSearchPage 클래스에서 만든 pageTitle 이라는 String 값을 보내게 됩니다.

그러면 PageBase 의 생성자를 볼까요?


여기서는 WebDriver 객체를 전달받은 파라미터 즉 여기서는 HtmlUnitDriver()를 사용해서 생성합니다. 그리고 pageTitle은 전달받은 pageTitle로 세팅을 하구요.


즉 GoogleSearchPage googleSearchPage = new GoogleSearchPage(new HtmlUnitDriver()); 이 한줄은 WebDriver를 생성하기 위해 만든건데요. 이 WebDriver를 만드는 기능이 GoogleSearchPage클래스와 PageBase 클래스에 나눠져 있고 그 기능들을 사용하는 겁니다.


그 다음에는 googleSerchPage.open()을 사용했는데요. 이 메소드는 PageBase에 있는 open() 메소드를 사용해서 drever.get()을 하도록 합니다.

여기서는 GoogleSearchPage 에서 정해진 URL 인 http://www.google.com으로 가는 겁니다.


그리고 enterSearchForm() 메소드를 호출하고 파라미터로 Cheese! 를 전달합니다.


이 enterSearchForm() 메소드는 GoogleSearchPage 클래스에 있죠?

이 전달된 Cheese! 값을 해당 페이지의 name 이 q 인 Element에 input 하는 겁니다.


그 다음에는 GoogleSearchResultPage 객체를 만들어서 거기에 GoogleSearchPage의 submitForm()메소드에서 return 된 값을 할당하구요. 


이 submitForm() 메소드는 해당 form 을 submit 하고 GoogleSearchResultPage() 생성자를 실행시켜서 그 결과값을 return 하는 거였죠?

GoogleSearchResultPage() 생성자는 해당 WebDriver와 pageTitle (여기서는 Google Search)을 파라미터로 PageBase 클래스에 전달해서 새롭게 WebDriver와 pageTitile을 만듭니다.


그 다음에는 새로 이동된 페이지의 Title을 을 get() 해서 콘솔에 뿌려주고 이 Title에 Cheese!라는 글자가 들어 있는지 테스트 합니다.


여기까지가 testGoogleSearch() 메소드가 동작하는 것을 분석한 겁니다.


이 작업은 page objects를 사용하지 않은 GoogleSearch 클래스의 testGoogleSearch()와 정확히 같은 동작을 합니다.


다만 GoogleSearch_withPageObject 클래스의 testGoogleSearch()는 page objects를 사용해서 그 기능을 여러 클래스에 분담해서 그 기능들을 가져다 쓰는 식으로 설게 된 겁니다.


다음에는 GoogleSearch_withPageObject 클래스의 testGoogleSearch()를 분석해 보겠습니다.

반응형


반응형

지난번 글에서 GoogleSearch 를 Page Object 를 사용해서 기능을 구현하기 위해 어떤 클래스들이 사용 됐는지 분석해 봤습니다.


PageBase.java

GoogleSearchPage.java

GoogleSearchResultPage.java

GoogleSearchSuggestPage.java

GoogleSearch_withPageObject.java


이렇게 5개의 클래스가 사용이 됐는데요.



가장 기본이 되는 PageBase.java 부터 분석해 보겠습니다.



/**
* Page Object base class. It provides the base structure
* and properties for a page object to extend.
*
* @author Chon Chung
*/
public class PageBase {
         /** Default URL */
         protected String URL;       
       
         /** This page's WebDriver */
         protected WebDriver driver;
       
         /** Expected Page Title. This will be used in isPageLoad()
         * to check if page is loaded. */
         protected String pageTitle;
       
       
         /** Constructor */
         public PageBase(WebDriver driver, String pageTitle) {
                 this.driver = driver;
                 this.pageTitle = pageTitle;
         }
       
         /**
         * Check if page is loaded by comparing
         * the expected page-title with an actual page-title.
         **/
         public boolean isPageLoad(){
                 return (driver.getTitle().contains(pageTitle));
         }
       
       
         /** Open the default page */
         public void open(){
                 driver.get(URL);
         }
       
       
         /** Returns the page title */
         public String getTitle() {
                 return pageTitle;
         }
       
         /** Returns the default URL */
         public String getURL() {
                return URL;
         }
       
         /**
         * Send text keys to the element that finds by cssSelector.
         * It shortens "driver.findElement(By.cssSelector()).sendKeys()".
         * @param cssSelector
         * @param text
         */
         protected void sendText(String cssSelector, String text) {
                        driver.findElement(By.cssSelector(cssSelector)).sendKeys(text);
         }
       
         /** Is the text present in page. */
         public boolean isTextPresent(String text){
                 return driver.getPageSource().contains(text);
         }
       
         /** Is the Element in page. */
         public boolean isElementPresent(By by) {
                        try {
                                driver.findElement(by);//if it does not find the element throw NoSuchElementException, thus returns false.
                                return true;
                        } catch (NoSuchElementException e) {
                                return false;
                        }
         }

         /**
         * Is the Element present in the DOM.
         *
         * @param _cssSelector                 element locater
         * @return                                        WebElement
         */
         public boolean isElementPresent(String _cssSelector){
                        try {
                                driver.findElement(By.cssSelector(_cssSelector));
                                return true;
                        } catch (NoSuchElementException e) {
                                return false;
                        }
         }
       

         /**
                * Checks if the elment is in the DOM and displayed.
                *
                * @param by - selector to find the element
                * @return true or false
                */
         public boolean isElementPresentAndDisplay(By by) {
                        try {                       
                                return driver.findElement(by).isDisplayed();
                        } catch (NoSuchElementException e) {
                                return false;
                        }
         }
       
         /**
         * Returns the first WebElement using the given method.        
         * It shortens "driver.findElement(By)".
         * @param by                 element locater.
         * @return                 the first WebElement
         */
         public WebElement getWebElement(By by){
                         return driver.findElement(by);                        
         }
}
/**
* Further reading:
* 1. Selenium webdriver page object:
*                 http://stackoverflow.com/questions/10315894/selenium-webdriver-page-object
* 2. Using Page Objects with Selenium and Web Driver 2.0
*                 http://www.summa-tech.com/blog/2011/10/10/using-page-objects-with-selenium-and-web-driver-20/
* 3. PageFactory
*                 http://code.google.com/p/selenium/wiki/PageFactory
*/



이 클래스에는 String 객체인 URL 과 pageTitle 그리고 WebDriver 객체인 driver 를 제일 먼저 초기화 합니다.

그리고 PageBase() 메소드를 보면 WebDriver와 pageTitle을 파라미터로 전달받아 각각 위에서 만들었던 WebDriver와 pageTitle 객체에 할당합니다.


다음에 나오는 isPageLoad() 메소드는 driver로 열은 페이지의 title에 해당 pageTitle이 있는지 확인해서 그 결과를 True or False로 반환합니다.

원하는 페이지가 open 된 것인지 확인할 때 사용하는 것 같습니다.


그리고 open() 메소드는 아까 setting 됐던 URL을 get() 메소드를 사용해서 브라우저에 open 시키는 메소드 입니다.


getTitle() 메소드는 아까 할당됐든 pageTitle을 return 해주고 getURL()은 URL을 return 합니다.


sendText()는 cssSelector와 text라는 스트링 파라미터를 전달 받습니다.

그리고 cssSelector 인 곳을 찾아 거기에 sendKey()메소드를 사용해서 text를 입력해 줍니다.


다음에 있는 isTextPresent() 메소드는 페이지내에 전달받은 text가 있는제 체크해서 그 결과를 boolean 타입으로 return 합니다.


그리고 isElementPresent() 는 text가 아니라 By 타입을 받아서 해당 Element 가 있는지 체크하고 그 결과를 boolean 타입으로 return 하구요.

그 아래는 isElementPresent() 를 OverLoading 한 것이네요. By 타입이 아니라 String 타입의 파라미터가 전달됐을 경우 실행됩니다.

하는일은 똑 같이 해당 Element 가 있는지 체크하고 그 결과를 boolean 타입으로 return 합니다.


이어서 isElementPresentAndDisplay()를 볼까요.

By 타입의 파라미터를 받아서 해당 Element가 display 돼 있는지 여부를 체크해서 boolean type을 return 합니다.


마지막으로 getWebElement() 메소드는 By 타입을 파라미터로 받아서 해당 element가 있는지 여부를 체크해서 boolean 타입을 반환하구요.


PageBase 클래스에는 이런 기능들이 있네요.

가장 기본적인 기능은 WebDriver 객체를 세팅하고 해당 페이지를 open (get()) 하는 기능이 되겠죠.






반응형


반응형

지난 글에서 다룬 GoogleSearch 클래스를 Page Object 를 사용해서 만드는 방법을 알아보겠습니다.


이렇게 Page Object를 사용하는 방법으로 사용하려면 그 기능별로 여러 클래스를 만들어야 합니다.


지난 GoogleSearch 클래스는 한개의 클래스에서 모든 작업이 이루어 졌기 때문에 한개의 JAVA 파일만 보면 됐습니다.

그런데 Page Object를 사용하는 버전에서는 여러개의 클래스 즉 여러개의 JAVA 파일을 봐야합니다.


우선 Page Object 를 사용하는 GoogleSearch 에는 어떤 클래스들이 있는지 알아보겠습니다.



우선 처음에 볼 부분은 GoogleSearch_withPageObject.java 입니다.


/**
* Google search demo class. This class shows how to use page objects.
* Compare with "GoogleSearch.java" class, and see how to use page objects.
*
* @author Chon Chung
*
*/
public class GoogleSearch_withPageObject {

    public static void main(String[] args) {
            GoogleSearch.testGoogleSearch();
            try{
                    GoogleSearch.testGoogleSuggest();
            }catch(Exception e){
                    System.out.println("Unable to perform testGoogleSuggest() - " +
                                                            e.getMessage());
            }
    }
   
    public static void testGoogleSearch(){
            //Create a new instance of GoogleSearch page
            GoogleSearchPage googleSearchPage = new GoogleSearchPage(new HtmlUnitDriver());

        // go to Google ("http://www.google.com")
            googleSearchPage.open();

        // Enter something to search for
            googleSearchPage.enterSearchForm("Cheese!");

        // Now submit the form, and get the next page object
            GoogleSearchResultPage googleSearchResultPage = googleSearchPage.submitForm();
              

        // Verify: Check the title of the page        
        String pageTitle = googleSearchResultPage.getTitle();
        System.out.println("Page title is: " + pageTitle);
        assertTrue("Got title: " + pageTitle, pageTitle.contains("Cheese!"));
    }
   
    public static void testGoogleSuggest() throws Exception {
               
        GoogleSearchSuggestPage googleSuggestPage = new GoogleSearchSuggestPage(new FirefoxDriver());
       
        // Go to the Google Suggest home page: "http://www.google.com/webhp?complete=1&hl=en"
        googleSuggestPage.open();
       
        // Enter the query string "Cheese", and get the list the suggestions
        List<WebElement> allSuggestions = googleSuggestPage.getSearchSuggestions("Cheese");
       
        for (WebElement suggestion : allSuggestions) {
            System.out.println(suggestion.getText());
        }
     }
}
/**
* Further reading:
* 1. Selenium webdriver page object:
*                 http://stackoverflow.com/questions/10315894/selenium-webdriver-page-object
* 2. Using Page Objects with Selenium and Web Driver 2.0
*                 http://www.summa-tech.com/blog/2011/10/10/using-page-objects-with-selenium-and-web-driver-20/
* 3. PageFactory
*                 http://code.google.com/p/selenium/wiki/PageFactory
* 4. Ben Burton's WebDriver Best Practices
*                 Video -- http://vimeo.com/44133409
* http://benburton.github.com/presentations/webdriver-best-practices/
*                
*/


main() 메소드는 이전의 GoogleSearch 클래스와 똑 같습니다.


testGoogleSearch() 메소드를 보면 GoogleSearch 에서는 먼저 WebDriver 객체를 만들고 그 객체를 사용해서 구글 페이지를 열었는데요.


이번에는 GoogleSearchPage 의 객체를 생성해서 사용합니다.


이 얘기는 GoogleSearchPage라는 클래스가 따로 있고 이 곳에서 WebDriver를 초기화 하는 작업을 한다는 얘기 입니다.



그러면 GoogleSearchPage.java를 볼까요?


/**
* Google Search Page Object.
* @author Chon Chung
*/
public class GoogleSearchPage extends PageBase{
       
        private final static String pageTitle = "Google";
       
        private final static String SEARCH_FIELD_NAME = "q";

        /** Constructor */
        public GoogleSearchPage(WebDriver driver){
                super(driver, pageTitle);

                // set the default URL
                URL = "http://www.google.com";
        }
       
        /** Returns the default URL */
        public String getURL(){
                return URL;
        }
       
        /** Enter the Search Text in the Search form field */
        public void enterSearchForm(String searchText){
                driver.findElement(By.name(SEARCH_FIELD_NAME)).sendKeys(searchText);                
        }
       
        /** Submit the form and return the next page object.
         * Seperate function should be in a seperate method. */
        public GoogleSearchResultPage submitForm(){
                driver.findElement(By.name(SEARCH_FIELD_NAME)).submit();
                return new GoogleSearchResultPage(driver);
        }
}


GoogleSearchPage()라는 생성자가 있죠? 여기에서 super 메소드를 사용합니다.

이 얘기는 이 클래스는 어떤 다른 클래스를 상속 받았다는 얘기입니다.

좀 위에 보니까 PageBase 클래스를 상속 받았네요.


소스 분석은 나중에 하고 오늘은 어떤 클래스들이 Page Object를 위해 사용됐는지 보기로 했으니까 다른 메소드들을 분석하는 것은 다음 글에서 하겠습니다.


그럼 PageBase 클래스를 보죠.


/**
* Page Object base class. It provides the base structure
* and properties for a page object to extend.
*
* @author Chon Chung
*/
public class PageBase {
         /** Default URL */
         protected String URL;       
       
         /** This page's WebDriver */
         protected WebDriver driver;
       
         /** Expected Page Title. This will be used in isPageLoad()
         * to check if page is loaded. */
         protected String pageTitle;
       
       
         /** Constructor */
         public PageBase(WebDriver driver, String pageTitle) {
                 this.driver = driver;
                 this.pageTitle = pageTitle;
         }
       
         /**
         * Check if page is loaded by comparing
         * the expected page-title with an actual page-title.
         **/
         public boolean isPageLoad(){
                 return (driver.getTitle().contains(pageTitle));
         }
       
       
         /** Open the default page */
         public void open(){
                 driver.get(URL);
         }
       
       
         /** Returns the page title */
         public String getTitle() {
                 return pageTitle;
         }
       
         /** Returns the default URL */
         public String getURL() {
                return URL;
         }
       
         /**
         * Send text keys to the element that finds by cssSelector.
         * It shortens "driver.findElement(By.cssSelector()).sendKeys()".
         * @param cssSelector
         * @param text
         */
         protected void sendText(String cssSelector, String text) {
                        driver.findElement(By.cssSelector(cssSelector)).sendKeys(text);
         }
       
         /** Is the text present in page. */
         public boolean isTextPresent(String text){
                 return driver.getPageSource().contains(text);
         }
       
         /** Is the Element in page. */
         public boolean isElementPresent(By by) {
                        try {
                                driver.findElement(by);//if it does not find the element throw NoSuchElementException, thus returns false.
                                return true;
                        } catch (NoSuchElementException e) {
                                return false;
                        }
         }

         /**
         * Is the Element present in the DOM.
         *
         * @param _cssSelector                 element locater
         * @return                                        WebElement
         */
         public boolean isElementPresent(String _cssSelector){
                        try {
                                driver.findElement(By.cssSelector(_cssSelector));
                                return true;
                        } catch (NoSuchElementException e) {
                                return false;
                        }
         }
       

         /**
                * Checks if the elment is in the DOM and displayed.
                *
                * @param by - selector to find the element
                * @return true or false
                */
         public boolean isElementPresentAndDisplay(By by) {
                        try {                       
                                return driver.findElement(by).isDisplayed();
                        } catch (NoSuchElementException e) {
                                return false;
                        }
         }
       
         /**
         * Returns the first WebElement using the given method.        
         * It shortens "driver.findElement(By)".
         * @param by                 element locater.
         * @return                 the first WebElement
         */
         public WebElement getWebElement(By by){
                         return driver.findElement(by);                        
         }
}
/**
* Further reading:
* 1. Selenium webdriver page object:
*                 http://stackoverflow.com/questions/10315894/selenium-webdriver-page-object
* 2. Using Page Objects with Selenium and Web Driver 2.0
*                 http://www.summa-tech.com/blog/2011/10/10/using-page-objects-with-selenium-and-web-driver-20/
* 3. PageFactory
*                 http://code.google.com/p/selenium/wiki/PageFactory
*/


PageBase() 생성자를 보시면 WebDriver와 pageTitle을 세팅합니다.

이렇게 3개의 클래스만 사용되나요?

아까 봤던 GoogleSearch_withPageObject 클래스의 testGoogleSearch() 메소드를 다시 보겠습니다.


좀 아래로 내려가니까 GoogleSearchResultPage 객체를 만드는 부분이 있습니다.

이 클래스도 따로 있는 것 같습니다.


/**
* Google Search Result Page Object.
* @author Chon Chung
*
*/
public class GoogleSearchResultPage extends PageBase{
       
        private static final String resultStats_id = "resultStats";
        private final static String pageTitle = "Google Search";
       
       
        public GoogleSearchResultPage(WebDriver driver){
                super(driver, pageTitle);        
        }
        
        /** Returns the search ResultStat. */
        public String getSearchResultStat(){
                return driver.findElement(By.id(resultStats_id)).getText();
        }  
}


여기서도 PageBase 클래스를 상속하네요.

이 PageBase 클래스는 저 위에 있으니까 다시 볼 필요는 없습니다.


그러면 testGoogleSearch() 메소드는 다 봤고 이어서 testGoogleSuggest() 메소드를 보겠습니다.

여기에서는 GoogleSearchSuggestPage 클래스에 대한 객체를 생성하네요.

이 GoogleSearchSuggestPage.java 파일도 사용됐습니다.


public class GoogleSearchSuggestPage extends PageBase{
        private final static String pageTitle = "Google";

        private final static String SEARCH_FIELD_NAME = "q";
       
        /** Constructor */
        public GoogleSearchSuggestPage(WebDriver driver){
                super(driver, pageTitle);

                // set the default URL
                URL = "http://www.google.com/webhp?complete=1&hl=en";
        }

        /** Enter the Search Text in the Search form field */
        public void enterSearchForm(String searchText){
                driver.findElement(By.name(SEARCH_FIELD_NAME)).sendKeys(searchText);                
        }
       
        /** Submit the form and return the next page object.
         * Seperate function should be in a seperate method. */
        public GoogleSearchResultPage submitForm(){
                driver.findElement(By.name(SEARCH_FIELD_NAME)).submit();
                return new GoogleSearchResultPage(driver);
        }

        /**
         * Enter the query string, and get the list of the suggestions
         * It use WaitTool to wait for the div (class=gsq_a).
         *
         * @param searchText the query string to search for
         * @return the list of the google search suggestions
         */
        public List<WebElement> getSearchSuggestions(String searchText){
        // Enter the query string "Cheese"
                driver.findElement(By.name(SEARCH_FIELD_NAME)).sendKeys(searchText);

        // Wait and get the list of suggestions
        List<WebElement> allSuggestions = WaitTool.waitForListElementsPresent(driver, By.className("gsq_a"), 5);
                return allSuggestions;
        }
}



여기서도 PageBase 클래스가 상속됐습니다. 다시 볼 필요는 없겠죠.

그 다음에 이 파일 안에서 다른 custom 클래스에 대한 객체를 만드는 부분은 없습니다.


다시 GoogleSearch_withPageObject 클래스로 돌아가죠.

testGoogleSuggest() 메소드를 보니까 다른 custom 클래스에 대한 객체를 생성하는 부분은 없습니다.


그러면 지금까지 살펴본 클래스들이 GoogleSearch.java 를 Page Object를 사용하는 버전으로 바꿨을 때 만든 클래스들 입니다.


PageBase.java

GoogleSearchPage.java

GoogleSearchResultPage.java

GoogleSearchSuggestPage.java

GoogleSearch_withPageObject.java


총 이렇게 5개의 파일이 사용됐습니다.


지난번 GoogleSearch.java에서는 단 한개의 파일만 사용했는데 똑 같은 일을 하는 Page Object를 만들기 위해 이렇게 파일을 5개로 쪼개서 기능을 부여 했네요.


다음 글에서는 각 클래스별로 어떤 일을 하고 전체적으로 어떻게 연관을 맺고 실행이 되는지를 알아보겠습니다.



반응형
이전 1 2 3 4 5 6 7 8 9 다음