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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

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()를 분석해 보겠습니다.

반응형