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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

Selenium WebDriver - PageObjects

2013. 10. 20. 02:53 | Posted by 솔웅


반응형

PageObjects는 Selenium WebDriver 테스트에서 권장하는 디자인 패턴입니다.

반복되는 코드를 패턴화 해서 하나로 만들어 놓고 활용하는 방법입니다.

제가 충분히 이해를 못 했는지 원문이 해석하기 무척 힘드네요. 일단 Tutorial 을 한번 대강 해석해 놓고 나중에 소스를 좀 분석해 보겠습니다.


==================================

PageObjects 



Page Object 패턴은 객체들의 시리즈로서 여러분의 웹앱 화면들을 represent 합니다.


WebDriver



Updated Mar 31, 2013




Page Objects

웹앱 UI에서 어떤 테스트해야할 부분이 있습니다. Page Object는 이러한 테스트해야할 부분들을 테스트 코드내에서 한 객체로서 간단하게 다룹니다. 이것은 반복되는 코드를 줄이고 ,UI가 바뀌었을 때 한 부분만 고치면 되도록 만듭니다.


Implementation Notes

PageObject는 동시에 두가지 방향을 생각하게 할 수 있습니다. 우선 한 방향은 테스트를 하는 개발자에게 향하는 것입니다. 특정 페이지에 의해 제공되는 서비스가 됩니다. 두번째는 개발자로부터 나오는 방향입니다. HTML 페이지의 구조에 대한 깊은 이해가 있어야 합니다. Page Object는 페이지가 요구하는 그런 서비스의요구로서 생각됩니다. 예를 들어 웹베이스 이메일 시스템의 inbox를 생각해 봅시다. 여기서 제공하는 기능들은 편지를 쓰거나 어떤 이메일을 읽거나 inbox 내 이메일들의 리스트를 보여주는 겁니다. 이러한 것들이 테스트를 하는데 있어서 어떻게 implement 될까요.



우리는 개발자에게 implementation 하는것 보다 interaction 하는 그런 서비스들에 대해 좀 더 생각하도록 해야하기 때문에 PageObject는 WebDriver 인스턴스의 기본적인 부분들 같은 것들을 그렇게 드러내지 않습니다. 이것을 구현하기 위해 PageObject의 메소드들은 다른 PageObject를 return 해야 합니다. 이 의미는 우리 어플리케이션을 통해 유저의 사용경험들을 효과적으로 모델화 할 수 있다는 겁니다. It also means that should the way that pages relate to one another change (like when the login page asks the user to change their password the first time they log into a service, when it previously didn't do that) simply changing the appropriate method's signature will cause the tests to fail to compile. Put another way, we can tell which tests would fail without needing to run them when we change the relationship between pages and reflect this in the PageObjects.



이 접근법의 결과로는 성공적인 로그인과 성공적이지 않은 로그인 보두에 대한 모델링이 필요하다는 겁니다. 혹은 앱의 상황에 따라서 click 의 결과가 다르게 나올 수 있다는 것이구요. PageObject에 여러 메소드들을 갖는것은 일반적인 것입니다.



public class LoginPage {
    public HomePage loginAs(String username, String password) {
        // ... clever magic happens here
    }
   
    public LoginPage loginAsExpectingError(String username, String password) {
        //  ... failed login here, maybe because one or both of the username and password are wrong
    }
   
    public String getErrorMessage() {
        // So we can verify that the correct error is shown
    }
}



위의 코드를 보면 아주 중요한 부분이 있습니다. 이 테스트들은 PageObjects가 아닙니다. 페이지의 상태에 대한 assertion들을 만들어야 합니다. 예를 들어



public void testMessagesAreReadOrUnread() {
    Inbox inbox = new Inbox(driver);
    inbox.assertMessageWithSubjectIsUnread("I like cheese");
    inbox.assertMessageWithSubjectIsNotUnread("I'm not fond of tofu");
}



이 코드는 아래와 같이 재 작성해야 합니다.



public void testMessagesAreReadOrUnread() {
    Inbox inbox = new Inbox(driver);
    assertTrue(inbox.isMessageWithSubjectIsUnread("I like cheese"));
    assertFalse(inbox.isMessageWithSubjectIsUnread("I'm not fond of tofu"));
}




물론 모든 guideline에는 예외가 있습니다. 그중에 PageObjects에서 흔히 볼 수 있는 것이 우리가 PageObject를 초기화 할 때 맞는 페이지에서 WebDriver가 있는지에 대해 체크하는 것입니다. 이 작업은 아래 예제에서와 같이 처리하시면 됩니다.


마지막으로 PageObject는 모든 페이지를 represent 할 필요는 없습니다. PageObjects는 그 사이트나 페이지 내에서 여러번 반복되는 부분을 represent 하면 됩니다. 여기서 핵심은 특정 페이지의 HTML 구조에 대한 지식을 기반으로 여러분의 test suite 내에서 한 부분에서 이것을 만들어 두면 된다는 겁니다.


Summary

    The public methods represent the services that the page offers
    페이지에서 요구되는 서비스들을 represent 하는 public methods
    Try not to expose the internals of the page
    페이지의 내부적인 부분을 표현하려고 하지 않는다.
    Generally don't make assertions
    일반적으로 assertion들을 만들지 않는다.
    Methods return other PageObjects
    다른 PageObjects를 return 한다.
    Need not represent an entire page
    전체 페이지를 represent 할 필요는 없다.
    Different results for the same action are modelled as different methods
    같은 action에 대한 다른 결과들을 위해 다른 메소드들로 모델링 한다.




Example



public class LoginPage {
    private final WebDriver driver;

    public LoginPage(WebDriver driver) {
        this.driver = driver;

        // Check that we're on the right page.
        if (!"Login".equals(driver.getTitle())) {
            // Alternatively, we could navigate to the login page, perhaps logging out first
            throw new IllegalStateException("This is not the login page");
        }
    }

    // The login page contains several HTML elements that will be represented as WebElements.
    // The locators for these elements should only be defined once.
        By usernameLocator = By.id("username");
        By passwordLocator = By.id("passwd");
        By loginButtonLocator = By.id("login");

    // The login page allows the user to type their username into the username field
    public LoginPage typeUsername(String username) {
        // This is the only place that "knows" how to enter a username
        driver.findElement(usernameLocator).sendKeys(username);

        // Return the current page object as this action doesn't navigate to a page represented by another PageObject
        return this;   
    }

    // The login page allows the user to type their password into the password field
    public LoginPage typePassword(String password) {
        // This is the only place that "knows" how to enter a password
        driver.findElement(passwordLocator).sendKeys(password);

        // Return the current page object as this action doesn't navigate to a page represented by another PageObject
        return this;   
    }

    // The login page allows the user to submit the login form
    public HomePage submitLogin() {
        // This is the only place that submits the login form and expects the destination to be the home page.
        // A seperate method should be created for the instance of clicking login whilst expecting a login failure.
        driver.findElement(loginButtonLocator).submit();

        // Return a new page object representing the destination. Should the login page ever
        // go somewhere else (for example, a legal disclaimer) then changing the method signature
        // for this method will mean that all tests that rely on this behaviour won't compile.
        return new HomePage(driver);   
    }

    // The login page allows the user to submit the login form knowing that an invalid username and / or password were entered
    public LoginPage submitLoginExpectingFailure() {
        // This is the only place that submits the login form and expects the destination to be the login page due to login failure.
        driver.findElement(loginButtonLocator).submit();

        // Return a new page object representing the destination. Should the user ever be navigated to the home page after submiting a login with credentials
        // expected to fail login, the script will fail when it attempts to instantiate the LoginPage PageObject.
        return new LoginPage(driver);  
    }

    // Conceptually, the login page offers the user the service of being able to "log into"
    // the application using a user name and password.
    public HomePage loginAs(String username, String password) {
        // The PageObject methods that enter username, password & submit login have already defined and should not be repeated here.
        typeUsername(username);
        typePassword(password);
        return submitLogin();
    }
}


Support in WebDriver

이 패턴을 지원하는 패키지로 PageFactory 가 있습니다. 이것은 PageObjects에서 boiler-plate 코드를 동시에 remove 하는 것을 도와 줍니다.


=================================================


사실 이 글을 읽고는 무엇인지 확실히 이해를 잘 못하겠구요. 그나마 저 소스코드를 보니까 대강 알 수 있을 것 같습니다.


이 LoginPage 클래스에서는 우선 생성자에서 여기가 login 페이지가 맞는지 확인하고 해당 페이지가 아니면 예외를 발생 시키네요.

이 클래스는 로그인 페이지를 위한 클래스 입니다.


그리고 By type의 객체 세개를 만들었는데 각각 id로 유저명, 비밀번호 그리고 login 버튼을 찾는 객체들입니다.


첫번째 메소드는 typeUsername 인데요. 여기서는 LoginPage를 return 하네요.

PageObjects는 다른 PageObjects를 return 한다던데 이것을 말하나 봅니다.

그 안에는 by id 로 유저명을 찾아서 거기다가 전달 받은 파라미터를 type 해 넣습니다.


두번째 메소드인 typePassword도 똑 같습니다. 여기는 비밀번호를 쳐 넣는겁니다.


그 다음 메소드인 submitLogin 이 나오는데요. 

여기서는 HomePage를 return 하네요.

메소드 내에서는 login button을 클릭하는 일을 하구요.


id하고 비밀번호를 입력하고 login 버튼을 클릭하게 되겠죠.


그 다음 메소드는 이전 메소드와 완전히 똑 같습니다. 단지 이름이 submitLoginExpectingFailure() 인걸로 봐서 fail이 예상될 때 호출하는 메소드 인가 봅니다.


그 다음 loginAs() 메소드가 실제 실행할 때 호출하는 메소드인데요.

username과 password를 파라미터로 받아서 typeUsername()과 typePassword() 를 호출해서 아이디와 비밀번호를 타입하고 return submitLogin() 함으로서 로그인 버튼을 클릭하고 성공한 페이지를 return 하는 일을 합니다.

이렇게 하면 로그인 기능은 어디서든지 불러서 사용할 수 있겠네요.


그러면 이 부분에 대한 반복되는 코딩을 막을 수 있구요.


아이디, 비밀번호 이외에 다른 정보가 들어가는 걸로 바뀌거나 아이디에 대한 룰이 바뀌거나 하면 이 부분만 바꿔주면 되구요.


이 패턴을 지원하는 패키지가 PageFactory 라고 하니까 다음 글에서는 이걸 한번 배워봐야겠습니다.


반응형

Selenium WebDriver Tutorial 03

2013. 10. 19. 02:44 | Posted by 솔웅


반응형

이 글은 Selenium WebDriver 튜토리얼을 3번에 걸쳐 번역한 글 중 하나입니다.

원본은 여기에 있습니다.



Driver Specifics and Tradeoffs



Selenium-WebDriver’s Drivers



WebDriver는 작성되어야 할 테스트에 대한 key interface 이름입니다. 여기에는 몇개의 implementation들이 있습니다. 그것들을 살펴보겠습니다.



HtmlUnit Driver

이것이 현재 가장 빠르고 가장 가벼운 implemeatation 입니다. 이것은 HtmlUnit에 기초하고 있습니다. HtmlUnit은 GUI 가 없는 WebBrowser의 자바 베이스 implementation입니다. 자바 이외의 언어는 이 드라이버를 사용하기 위해 Selemium Server를 binding 해야 합니다.


Usage


WebDriver driver = new HtmlUnitDriver();


장점

    WebDriver를 가장 빨리 implementation 함
    pure Java solution으로 플랫폼 독립적이다.
    자바스크립트를 지원함


단점

    다른 브라우저의 자바스크립트 behaviour 를 Emulate 해야 한다.

   



JavaScript in the HtmlUnit Driver


주요 브라우저들은 HtmlUnit (Rhino)에 의해 사용되는 자바스크립트 엔진을 사용하지 않습니다. HtmlUnit을 사용하는 자바스크립트를 테스트 한다면 이 브라우저들에서는 그 결과가 다르게 나올 겁니다.


우리가 자바스크립트라고 말할 때 실제 이 의미는 자바스크립트와 DOM을 말하는 겁니다. DOM 이 W3C에 의해 정의 됐음에도 불구하고 각 브라우저들은 이 DOM의 implementation과 관련해서 자기들만의 방법을 사용하고 있습니다. 그래서 그에 따라 자바스크립트가 어떻게 작동하는지도 서로 다릅니다. HtmlUnit은 DOM을 implementation 하는 아주 확실한 방법입니다. 그리고 자바스크립트를 사용하기 위한 좋은 방법을 지원하고 있습니다. 하지만 다른 브라우저들도 그렇기는 합니다. W3C 표준과 여러 주요 브라우저들의 implementation들은 서로 다릅니다.


WebDriver와 관련해서 우리는 선택을 해야 합니다. HtmlUnit의 자바스크립트를 사용해서 팀원들이 실행할 때 생길 수 있는 위험성을 감수해야 할지 아니면 자바스크립트를 disable 해야 하는지요. 디폴트로는 HtmlUnit을 사용할 때 자바스크립트를 default로 함으로서 피해를 줄일수도 있겠죠. WebDriver와 HtmlUnit 모두를 release 하는데 있어 우리는 특정 부분에서 HtmlUnit의 디폴트로 자바스크립트를 enable 로 하기를 바랍니다.


Enabling JavaScript


그때까지 기다리기 힘드시면 자바스크립트를 enable 하도록 만드는것은 굉장히 쉽습니다.


HtmlUnitDriver driver = new HtmlUnitDriver(true);


이렇게 하면 HtmlUnit Driver에서 디폴트로 파이어폭스 3.6 버전의 자바스크립트를 emulate 합니다.



Firefox Driver

Firefox 플러그인을 사용해서 파이어폭스 브라우저를 사용할 수 있습니다. 사용되는 Firefox 프로파일은 Selenium WebDriver.xpi (플러그인)을 포함합니다. A few settings are also changed by default ((see the source to see which ones) ) Firefox Driver는 윈도우, 맥, 리눅스에서 실행하고 테스트할 수 있습니다.


Usage


WebDriver driver = new FirefoxDriver();


장점

    실제 브라우저에서 실행하고 자바스크립트를 지원함
    인터넷 익스플로러 Driver 보다 빠르다.

단점

    HtmlUnit Driver 보다 느리다.

   


Modifying the Firefox Profile


user agent string을 수정할 수도 있습니다. 그러려면 여러 유용한 extension들을 포함하고 있는 파이어폭스 프로파일을 trick out 해야 합니다. 이 프로파일을 obtain하는데는 두가지 방법이 있습니다. Firefox의 프로파일 매니저에 의해 프로파일이 생성됐다고 가정하고 아래 코드를 보겠습니다. (firefox -ProfileManager):


ProfilesIni allProfiles = new ProfilesIni();
FirefoxProfile profile = allProfiles.getProfile("WebDriver");
profile.setPreferences("foo.bar", 23);
WebDriver driver = new FirefoxDriver(profile);


프로파일이 Firefox로 register 되지 않았을 경우는 이렇게 합니다.


File profileDir = new File("path/to/top/level/of/profile");
FirefoxProfile profile = new FirefoxProfile(profileDir);
profile.addAdditionalPreferences(extraPrefs);
WebDriver driver = new FirefoxDriver(profile);


리눅스에서 Firefox에 native event들이 디폴트로 disable되어 있다면 다음과 같이 enable 되게 할 수 있습니다.


FirefoxProfile profile = new FirefoxProfile();
profile.setEnableNativeEvents(true);
WebDriver driver = new FirefoxDriver(profile);


Info


See the Firefox section in the wiki page for the most up to date info.


Internet Explorer Driver


이 driver는 .dll에 의해 콘트롤 됩니다. 이것은 Windows OS에서만 사용 가능합니다. Selenium 은 XP 의 버전 6,7,8 과 Windows7 상의 버전 9 에 맞게 만들었습니다.


Usage


WebDriver driver = new InternetExplorerDriver();


Pros

    실제 브라우저에서 실행되고 IE가 지원하는 자바스크립트를 지원한다.


Cons

    IE는 윈도우즈에서만 작동한다.
    비교적 느리다.
    대부분의 버전에서 native 하게 XPath를 지원하지 않는다. 다른 브라우저보다 느린 Sizzle이 자동적으로 inject 된다. 어떤 브라우저에서는 CSS selector와 비교할 때 더 느리다.
    버전 6,7에서는 CSS 가 native 적으로 지원하지 않는다. 대신 Sizzle이 inject 된다.
    IE 8,9에서 CSS selector는 native 이다. 하지만 그 브라우저에서는 CSS3를 완전히 지원하지 않는다.


Info

See the Internet Explorer section of the wiki page for the most up to date info. Please take special note of the Required Configuration section.



Chrome Driver


Chrome Driver는 Chromium project에 의해 유지되고 지원됩니다. WebDriver는 chromedriver binary를 통해서 Chrome과 함께 작동됩니다. (found on the chromium project’s download page). 이것을 사용하려면 chromedriver와 인스톨 된 chrome browser가 모두 있어야 합니다. chromedriver는 여러분 컴퓨터 내의 어딘가에 있어야 합니다. 그리고 그 경로를 세팅해서 WebDriver가 자동적으로 찾을 수 있도록 해야 합니다. Chrome browser는 chromedriver에 의해 인스톨된 경로를 자동으로 찾을 겁니다. 이런 것들은 환경변수로 overridden 할 수 있습니다. Please refer to the wiki for more information.


Usage


WebDriver driver = new ChromeDriver();



Pros

    실제 브라우저에서 동작되고 자바스크립트를 지원한다.
    Chrome이 Webkit-based 브라우저이기 때문에 Chrome Driver는 Safari에서 동작하는 것들에 대해서도 verify할 수 있습니다. Chrome은 V8 자바스크립트 엔진을 사용하고Safari는 Nitro 엔진을 사용합니다. 그러니까 JavaScript가 실행되는 것이 서로 다를 수도 있습니다.



Cons

    HtmlUnit Driver 보다 느리다.



Info


See our wiki for the most up to date info. More info can also be found on the downloads page



Getting running with Chrome Driver

Download the Chrome Driver executable and follow the other instructions on the wiki page



Opera Driver

See the Opera Driver wiki article in the Selenium Wiki for information on using the Opera Driver.

iPhone Driver

See the iPhone Driver wiki article in the Selenium Wiki for information on using the Mac iOS Driver.

Android Driver

See the Android Driver wiki article in the Selenium Wiki for information on using the Android Driver.




Alternative Back-Ends: Mixing WebDriver and RC Technologies



WebDriver-Backed Selenium-RC



자바버전의 WebDriver는 Selenium-RC API의 implementation을 제공합니다. 이 의미는 여러분이 Selenium-RC API에 있는 기능들을 WebDriver에서도 사용할 수 있다는 겁니다. 즉 이전 버전의 기능들도 모두 사용할 수 있다는 것이죠. 이미 Selenium-RC API를 사용한 테스트 suite들을 가지고 있는 분들이 WebDriver의 기능들을 사용해야 하는 분들에게 좋은 소식이죠. 이것을 Selenium-WebDriver로 migration을 쉽게 해 줍니다. 또한 두 API 모두를 한번에 사용할 수도 있습니다.


Selenium-WebDriver is used like this:


// You may use any WebDriver implementation. Firefox is used here as an example
WebDriver driver = new FirefoxDriver();

// A "base url", used by selenium to resolve relative URLs
 String baseUrl = "http://www.google.com";

// Create the Selenium implementation
Selenium selenium = new WebDriverBackedSelenium(driver, baseUrl);

// Perform actions with selenium

selenium.open("http://www.google.com");
selenium.type("name=q", "cheese");
selenium.click("name=btnG");

// Get the underlying WebDriver implementation back. This will refer to the
// same WebDriver instance as the "driver" variable above.
WebDriver driverInstance = ((WebDriverBackedSelenium) selenium).getWrappedDriver();

//Finally, close the browser. Call stop on the WebDriverBackedSelenium instance
//instead of calling driver.quit(). Otherwise, the JVM will continue running after
//the browser has been closed.
selenium.stop();


Pros

    WebDriver와 Selenium API를 함께 사용할 수 있다.
    Selenium RC API에서 WebDriver API로 migration 하는 간단한 방법을 제공한다.
    실행하기 위해 Selenium RC server를 필요로 하지 않는다.


Cons

    Does not implement every method
    모든 메소드를 implement 하지는 않는다.
    좀 더 advanced 한 Selenium 사용기능이 제대로 작동하지 않는다. (using “browserbot” or other built-in JavaScript methods from Selenium Core)
    어떤 메소드는 서로 다른 implementation으로 인해 느려질 수 있다.


   
Backing WebDriver with Selenium


WebDriver는 Selenium RC 보다 적은 브라우저들을 지원합니다. 그래서 Selenium RC 버전을 WebDriver API 버전으로 사용하려면 SeleneseCommandExecutor를 사용할 수 있습니다.



Safari는 이와 같은 방법으로 지원되는데요. 다음과 같이 코딩하면 됩니다. (be sure to disable pop-up blocking):


DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setBrowserName("safari");
CommandExecutor executor = new SeleneseCommandExecutor(new URL("http://localhost:4444/"), new URL("http://www.google.com/"), capabilities);
WebDriver driver = new RemoteWebDriver(executor, capabilities);


이렇게 접근하는 방법에는 중요한 몇가지 제한이 있습니다. findElements가 원하는대로 작동 안될 수도 있습니다. 또한 Selenium Core를 사용하기 때문에 자바스크립트 sandbox도 제한이 있습니다.


Running Standalone Selenium Server for use with RemoteDrivers


Selenium’s Download page에서 download selenium-server-standalone-<version>.jar 를 다운받으시고 옵션으로 IEDriverServer도 다운 받습니다. Chrome을 사용하려면 Google Code에서 다운 받으세요.


IEDriverServer와 chromedriver를 unpack 하고 이것을 $PATH / %PATH% 에 넣으세요. 그러면 추가적인 수정없이 IE와 Chrome 을 사용하실 수 있습니다.


Start the server on the command line with


java -jar <path_to>/selenium-server-standalone-<version>.jar


If you want to use native events functionality, indicate this on the command line with the option


-Dwebdriver.enable.native.events=1


For other command line options, execute


java -jar <path_to>/selenium-server-standalone-<version>.jar -help


In order to function properly, the following ports should be allowed incoming TCP connections: 4444, 7054-5 (or twice as many ports as the number of concurrent instances you plan to run). Under Windows, you may need to unblock the applications as well.



Additional Resources


WebDriver에 대해 좀 더 많은 것을 아시려면 WebDriver’s wiki를 보세요.


그리고 인터넷에서 Selenium-WebDriver’s drivers를 포함해서 Selenium과 관련된 여러 정보를 찾아보세요. Selenium과 관련한 여러 블로그와 포럼들이 있을 겁니다. 참고로 Selenium 사용자 그룹에도 정보가 아주 많습니다. http://groups.google.com/group/selenium-users



Next Steps.


이글은 WebDriver를 high level로 간단히 훑어 본 겁니다. 일단 Selenium-WebDriver API에 익숙해 지면 그 다음에는 어떻게 test suite들을 만드는지에 대해 궁금하실 겁니다. 요즈음 Selenium 전문가들이 접근하는 방법은 Page Factory를 사용해서 Page Object Design Pattern을 사용해서 테스트 코드를 디자인 하는 겁니다. Selenium-WebDriver는 자바와 C#에서 PageFactory 를 지원합니다. 이것은 next chapter에서  advanced topic을 다루는 글에서 다뤄집니다. 이 기술과 관련한 high-level description에 대해서는 Test Design Considerations chapter에서 다뤄 집니다. 이 두 글들은 여러분의 테스트 코드를 좀 더 유지보수하기 쉽도록 작성하는 방법을 알려 줍니다.

반응형


반응형

Selenium WebDriver 와 TestNG 를 같이 사용하는 방법을 정리하겠습니다. 그리고 xml을 이용해서 parallel 하게 같은 테스트를 여러 브라우저에서 같이 테스트 하는 방법도 알아보구요.


우선 실제 Test를 하는 클래스부터 봅니다.


public class WebDriverTest extends SelTestCase {
    @Test
    public void testWiki() {
        driver.get("http://www.wikipedia.org/");
        driver.findElement(By.id("searchInput")).sendKeys("Selenium Software");
        driver.findElement(By.name("go")).click();
        driver.findElement(By.cssSelector(".mw-search-results li:nth-of-type(1) a")).click();
        Assert.assertTrue(
                driver.findElement(
                        By.cssSelector("img[src='//upload.wikimedia.org/wikipedia/en/5/5c/Seleniumlogo.png']")).
                        isDisplayed());
        Reporter.log("My test Passed");
    }
}


이 WebDriverTest는 SelTestCase를 extends 했습니다. 이 SelTestCase는 나중에 보고 이 소스부터 분석하겠습니다.

@Test 는 annotation 인데요. jUnit 이랑 똑같습니다. 그런데 여기서는 TestNG를 사용할 거니까 TestNG를 import 해야 합니다.


import org.testng.annotations.Test;


다음은 testWiki()라는 테스트 메소드를 만듭니다.

여기서는 wikipedia.org 페이지를 열고 id 가 searchInput 인 element를 찾아서 거기에 Selenium Software 를 입력합니다. 아마 이 element는 textfield나 textarea 일 겁니다.

그 다음엔 name 이 go 인 엘리먼트를 click 합니다.

그 다음엔 cssSelector를 사용하는데요. class 가 mw-search-results 인것을 찾고 그 안에 li 인 것 중에 가장 첫번째 li에 있는 a 링크를 찾아서 그 링크를 클릭합니다.

그 다음에 테스트 문이 들어가는데요. 링크를 클릭해서 나온 페이지에 해당 이미지가 있으면 테스트가 Success 될 것이고 이 이미지가 없으면 fail 될 겁니다.

그 다음에는 TestNG에서 자동으로 생성해 주는 Report 에 log가 찍히도록 해 줍니다.


그런데 여기서 driver라는 객체가 있는데 이 객체를 선언해 주는 부분이 없습니다.

이 driver 객체는 상속받은 SelTestCase 클래스에서 선언해 주었을 겁니다.


public class SelTestCase {
    WebDriver driver;
   
    @BeforeMethod()
    @Parameters("browser")
    public void launchBrowser(String browser) {
        String path = System.getProperty("user.dir"); // current path of project

        if(browser.equalsIgnoreCase("FF")){
            driver = new FirefoxDriver();
        }else if(browser.equalsIgnoreCase("Chrome")){
            System.setProperty("webdriver.chrome.driver",path + "\\chromedrive\\chromedriver.exe");
            driver = new ChromeDriver();
        }else if (browser.equalsIgnoreCase("IE")) {
            System.setProperty("webdriver.ie.driver",path + "\\iedrive\\IEDriverServer.exe");
            driver = new InternetExplorerDriver();
        }
    }
   
    @AfterMethod
    public void closeBrowser() {
        driver.close();
        driver.quit();
    }
}

이 SelTestCase 클래스에서는 맨 먼저 WebDriver 를 초기화 시킵니다. 여기서 초기화 


그 다음에 @BeforeMethod() annotation이 있는데요. 이것은 테스트 메소드가 실행되기 전에 실행하라는 의미 입니다.

그러니까 WebDriverTest 클래스에 있는 testWiki() 라는 테스트 메소드가 시작되기 전에 이 메소드가 시작 됩니다.


그 다음에 있는 annotation은 파라미터로서 browser를 받겠다는 건데요. 이 파라미터는 다음에 볼 xml 파일에서 던져 줄 겁니다.


해당 메소드를 보면은요. launchBrowser() 라는 메소드인데요 스트링을 input 파라미터로 받습니다.

그 안을 보면 path라는 스트링에 이 프로젝트의 현재 위치를 대입합니다.


그리고 if 문이 나오는데요. input 파라미터가 FF 이면 FirefoxDriver() 를 driver 객체에 대입하고 Chrome인 경우는 ChromeDriver()를 그리고 IE인 경우에는 인터넷 익스플로러 드라이버를 drive 객체에 대입합니다.


그러니까 저 위에 있는 WebDriverTest 클래스의 testWiki() 테스트 메소드가 시작되기 전에 조건에 맞게 driver 객체를 세팅한 후 그 테스트 메소드가 실행될 겁니다.


다음에 나오는 건 @AfterMethod 가 있는 걸로 봐서 저 testWiki() 테스트 메소드가 실행되고 난 다음에 실행되는 메소드 입니다.

이름이 closeBrower() 이고 그 안에는 브라우저를 close하고 메모리로부터 driver 객체를 완전히 없애는 quit을 해주는 메소드 입니다.


이 두 클래스에서는 TestNG와 Selenium WebDriver를 같이 사용하고 있습니다.


여기에서 이 한개의 테스트를 여러 웹 브라우저에서 테스트할 수 있도록 하는 작업을 하겠습니다.

그러면 해당 프로젝트에 xml 파일을 만들어야 합니다.


testng.xml 이라는 파일을 아래와 같이 만듭니다.


<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Widipedia Test" verbose="3" parallel="methods">
    <test name="Wiki in FF">
    <parameter name="browser" value="FF"></parameter>
        <classes>
            <class name="WebDriverTest"/>
        </classes>
    </test>
   
    <test name="Wiki in Chrome">
    <parameter name="browser" value="Chrome"></parameter>
        <classes>
            <class name="WebDriverTest"/>
        </classes>
    </test>
    <!--
    <test name="Wiki in IE">
    <parameter name="browser" value="IE"></parameter>
        <classes>
            <class name="WebDriverTest"/>
        </classes>
    </test>   
     -->
</suite>

<suite> 태그를 써서 여러 테스트를 한개의 suite에 담겠다는 것을 알립니다.

그리고 <test> 태그 안에 각각의 테스트들을 정의 합니다.

첫번째는 <parameter> 태그안에 name 이 browser이고 value 는 FF 를 설정합니다.

이 것이 SelTestCase 클래스에 있는 launchBrowser()메소드에 전달될 파라미터 입니다.

그 다음에 <classes> 태그 안에는 name 에  WebDriverTest 를 넣습니다.

이 클래스를 run 하라는 겁니다.


그 다음에는 Chrome 과 IE 에 대한 <test> 태그들을 같은 방식으로 만듭니다.


여기서 IE는 주석처리를 했습니다.

제가 해 보니까 에러가 나더라구요.

혹시 이 IE는 왜 에러가 나는지 아시는 분은 좀 알려 주세요.


이 test suite를 실행하려면 Run-run configurations 를 선택합니다.




여기서 보시다시피 프로젝트를 선택하고 Suite를 선택한 다음에 아까 만들었던 xml 을 세팅합니다.


그러면 이 xml에서 정의된 대로 테스트가 여러 브라우저에서 실행될 겁니다.



반응형