Selenium의 자동화: 페이지 개체 모델 및 페이지 팩토리
게시 됨: 2022-03-11자동화된 테스트를 작성하는 것은 애자일 소프트웨어 개발 팀에게 단순한 사치 이상의 일입니다. 소프트웨어 개발 주기의 초기 단계에서 신속하게 버그를 찾는 데 필요한 필수 도구입니다. 아직 개발 단계에 있는 새로운 기능이 있는 경우 개발자는 자동화된 테스트를 실행하고 시스템의 다른 부분이 이러한 변경에 의해 어떻게 영향을 받는지 확인할 수 있습니다. 이 기사에서는 Selenium에서 Page Object 모델을 사용하여 자동화된 테스트 속도를 높이는 방법을 설명합니다.
테스트 자동화를 통해 버그 수정 비용을 낮추고 소프트웨어 품질 보증(QA) 프로세스를 전반적으로 개선할 수 있습니다. 적절한 테스트를 통해 개발자는 QA에 도달하기 전에도 버그를 찾아 해결할 수 있습니다. 테스트 자동화는 지속적으로 회귀하는 테스트 사례와 기능을 자동화하는 데 도움이 됩니다. 이렇게 하면 QA가 애플리케이션의 다른 부분을 테스트하는 데 더 많은 시간을 할애할 수 있습니다. 또한 이는 생산 릴리스에서 제품의 품질을 보장하는 데 도움이 됩니다. 결과적으로 우리는 보다 효과적으로 안정적인 제품과 보다 효율적인 QA 프로세스를 얻습니다.
자동화된 테스트를 작성하는 것은 개발자와 엔지니어에게 쉬운 작업처럼 보일 수 있지만, 여전히 제대로 구현되지 않은 테스트로 끝날 가능성이 있고 모든 애자일 프로세스에서 코드 유지 관리 비용이 많이 듭니다. 애자일 개발 프로젝트에서 변경 사항이나 기능을 지속적으로 제공하려는 시도는 테스트가 포함될 때 비용이 많이 들 수 있습니다. 20개의 테스트가 의존하는 웹 페이지에서 하나의 요소를 변경하려면 이 20개의 테스트 루틴을 거쳐 새로 도입된 변경 사항에 적응하도록 각각을 업데이트해야 합니다. 이는 시간이 많이 소요될 뿐만 아니라 자동화된 테스트를 조기에 구현하는 것과 관련하여 심각한 동기부여 요인이 될 수 있습니다.
그러나 한 곳에서만 변경할 수 있고 모든 관련 테스트 루틴에서 이를 사용할 수 있다면 어떨까요? 이 기사에서는 Selenium의 자동화된 테스트를 살펴보고 페이지 개체 모델을 사용하여 유지 관리 및 재사용 가능한 테스트 루틴을 작성하는 방법을 살펴보겠습니다.
Selenium의 페이지 개체 모델
페이지 객체 모델은 웹 페이지가 클래스로 표현되고 페이지의 다양한 요소가 클래스의 변수로 정의되는 Selenium의 객체 디자인 패턴입니다. 그런 다음 가능한 모든 사용자 상호 작용을 클래스의 메서드로 구현할 수 있습니다.
clickLoginButton(); setCredentials(user_name,user_password);
클래스에서 잘 명명된 메서드는 읽기 쉽기 때문에 읽기 쉽고 향후 유지 관리 또는 업데이트가 더 쉬운 테스트 루틴을 구현하는 우아한 방법으로 작동합니다. 예를 들어:
Page Object 모델을 지원하기 위해 Page Factory를 사용합니다. Selenium의 Page Factory는 Page Object의 확장이며 다양한 방법으로 사용할 수 있습니다. 이 경우 페이지 팩토리를 사용하여 웹 페이지 클래스 또는 페이지 개체에 정의된 웹 요소를 초기화합니다.
웹 요소를 포함하는 웹 페이지 클래스 또는 페이지 개체는 웹 요소 변수를 사용하기 전에 Page Factory를 사용하여 초기화해야 합니다. 이것은 PageFactory에서 initElements 함수를 사용하여 간단하게 수행할 수 있습니다.
LoginPage page = new LoginPage(driver); PageFactory.initElements(driver, page);
또는 더 간단합니다.
LoginPage page = PageFactory.intElements(driver,LoginPage.class)
또는 웹 페이지 클래스 생성자 내부에서:
public LoginPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); }
Page Factory는 구성된 "로케이터"를 기반으로 실제 웹 페이지의 해당 요소에 대한 참조로 모든 WebElement 변수를 초기화합니다. 이것은 @FindBy 주석을 사용하여 수행됩니다. 이 주석을 사용하여 요소를 식별하는 데 필요한 정보와 함께 요소를 찾는 전략을 정의할 수 있습니다.
@FindBy(how=How.NAME, using="username") private WebElement user_name;
이 WebElement 변수에서 메서드가 호출될 때마다 드라이버는 먼저 현재 페이지에서 메서드를 찾은 다음 상호 작용을 시뮬레이션합니다. 간단한 페이지로 작업하는 경우 찾을 때마다 페이지에서 요소를 찾을 수 있다는 것을 알고 있으며 결국 이 페이지를 탐색하고 돌아오지 않을 것임을 알고 캐시할 수 있습니다. 다른 간단한 주석을 사용하여 조회된 필드:
@FindBy(how=How.NAME, using="username") @CacheLookup private WebElement user_name;
WebElement 변수의 이 전체 정의는 훨씬 더 간결한 형식으로 대체될 수 있습니다.
@FindBy(name="username") private WebElement user_name;
@FindBy 주석은 일을 좀 더 쉽게 만드는 몇 가지 다른 전략을 지원합니다.
id, name, className, css, tagName, linkText, partialLinkText, xpath
@FindBy() private WebElement user_name; @FindBy(name="passsword") private WebElement user_password; @FindBy(className="h3") private WebElement label; @FindBy(css=”#content”) private WebElement text;
초기화되면 이러한 WebElement 변수를 사용하여 페이지의 해당 요소와 상호 작용할 수 있습니다. 예를 들어 다음 코드는 다음과 같습니다.
user_password.sendKeys(password);
... 주어진 키 입력 순서를 페이지의 비밀번호 필드로 보내면 다음과 같습니다.
driver.findElement(By.name(“user_password”)).sendKeys(password);
계속 진행하다 보면 페이지에서 요소 목록을 찾아야 하는 상황을 자주 접하게 되며 이때 @FindBys 가 유용합니다.
@FindBys(@FindBy(css=”div[class='yt-lockup-tile yt-lockup-video']”))) private List<WebElement> videoElements;
위의 코드는 두 개의 클래스 이름이 "yt-lockup-tile" 및 "yt-lockup-video"인 모든 div 요소를 찾습니다. 이것을 다음과 같이 바꾸면 훨씬 더 단순화할 수 있습니다.
@FindBy(how=How.CSS,using="div[class='yt-lockup-tile yt-lockup-video']") private List<WebElement> videoElements;
또한 @FindAll 을 여러 @FindBy 주석과 함께 사용하여 주어진 위치 지정자와 일치하는 요소를 찾을 수 있습니다.
@FindAll({@FindBy(how=How.ID, using=”username”), @FindBy(className=”username-field”)}) private WebElement user_name;
이제 웹 페이지를 Java 클래스로 표시하고 Page Factory를 사용하여 WebElement 변수를 쉽게 초기화할 수 있으므로 Page Object 패턴과 Page Factory를 사용하여 간단한 Selenium 테스트를 작성하는 방법을 볼 차례입니다.
Java의 간단한 Selenium 테스트 자동화 프로젝트
페이지 개체 모델 자습서의 경우 개발자가 Toptal에 등록하는 것을 자동화해 보겠습니다. 그렇게 하려면 다음 단계를 자동화해야 합니다.
www.toptal.com 방문
"개발자 지원" 버튼을 클릭합니다.
포털 페이지에서 먼저 열려 있는지 확인하십시오.
"Total 가입" 버튼을 클릭합니다.
양식을 작성하시오
"Join Toptal" 버튼을 클릭하여 양식을 제출하십시오.
프로젝트 설정
자바 JDK 다운로드 및 설치
InteliJ Idea 다운로드 및 설치
새 Maven 프로젝트 만들기
"Project SDK"를 JDK에 연결합니다(예: Windows의 경우 "C:\Program Files\Java\jdkxxx").
groupId 및 artifactId 설정:
<groupId>SeleniumTEST</groupId> <artifactId>Test</artifactId>
- 프로젝트 POM 파일에 종속성 Selenium 및 JUnit Maven 추가
<dependencies> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- Selenium --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-firefox-driver</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-support</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>${selenium.version}</version> </dependency> </dependencies>
Selenium 버전 및 JUnit 버전을 Google 및 Selenium 사이트에서 JUnit Maven을 검색하여 찾을 수 있는 최신 버전 번호로 교체하십시오.
이 시점에서 자동 빌드가 활성화되면 종속성이 자동으로 다운로드를 시작해야 합니다. 그렇지 않은 경우 IntelliJ Idea IDE의 오른쪽에 있는 Maven 프로젝트 패널에서 플러그인 > 설치 > 설치:설치를 활성화하면 됩니다.
프로젝트가 부트스트랩되면 "src/test/java"에서 테스트 패키지 생성을 시작할 수 있습니다. 패키지 이름을 "com.toptal"로 지정하고 그 아래에 "com.toptal.webpages" 및 "com.toptal.tests"라는 두 개의 패키지를 더 만듭니다.
Page Object/Page Factory 클래스를 "com.toptal.webpages" 아래에 유지하고 테스트 루틴을 "com.toptal.tests" 아래에 유지합니다.
이제 Page Object 클래스 생성을 시작할 수 있습니다.
홈페이지 페이지 개체
가장 먼저 구현해야 할 것은 Toptal의 홈페이지(www.toptal.com)입니다. "com.toptal.webpages" 아래에 클래스를 만들고 이름을 "HomePage"로 지정합니다.
package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.How; import org.openqa.selenium.support.PageFactory; public class HomePage { private WebDriver driver; //Page URL private static String PAGE_URL="https://www.toptal.com"; //Locators //Apply as Developer Button @FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER") private WebElement developerApplyButton; //Constructor public HomePage(WebDriver driver){ this.driver=driver; driver.get(PAGE_URL); //Initialise Elements PageFactory.initElements(driver, this); } public void clickOnDeveloperApplyButton(){ developerApplyButton.click(); } }
요소 보관처 결정
Toptal의 홈페이지에서 우리는 특히 한 가지 요소에 관심이 있습니다. 바로 "개발자 지원" 버튼입니다. 위의 작업과 같이 텍스트를 일치시켜 이 요소를 찾을 수 있습니다. 웹 페이지를 Page Object 클래스로 모델링하는 동안 요소를 찾고 식별하는 것이 종종 일이 될 수 있습니다. Google Chrome 또는 Firefox의 디버깅 도구를 사용하면 이 작업을 더 쉽게 수행할 수 있습니다. 페이지의 요소를 마우스 오른쪽 버튼으로 클릭하면 컨텍스트 메뉴에서 "요소 검사" 옵션을 활성화하여 요소에 대한 자세한 정보를 찾을 수 있습니다.

한 가지 일반적인(그리고 내가 선호하는) 방법은 Selenium의 Firefox 웹 드라이버와 함께 Firefox의 FireBug 확장을 사용하여 요소를 찾는 것입니다. FireBug 확장을 설치하고 활성화한 후 페이지를 마우스 오른쪽 버튼으로 클릭하고 "FireBug로 요소 검사"를 선택하여 FireBug를 열 수 있습니다. FireBug의 HTML 탭에서 페이지에 있는 모든 요소의 XPath, CSS 경로, 태그 이름 또는 "Id"(사용 가능한 경우)를 복사할 수 있습니다.
위 스크린샷에 있는 요소의 XPath를 복사하여 다음과 같이 페이지 개체에 해당 요소에 대한 WebElement 필드를 만들 수 있습니다.
@FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") WebElement heading;
또는 간단하게 유지하기 위해 여기에서 관심 있는 요소를 고유하게 식별하는 태그 이름 "h1"을 사용할 수 있습니다.
@FindBy(tagName = "h1") WebElement heading;
DeveloperPortalPage 페이지 개체
다음으로, "Apply As A Developer" 버튼을 클릭하여 도달할 수 있는 개발자 포털 페이지를 나타내는 페이지 개체가 필요합니다.
이 페이지에는 두 가지 흥미로운 요소가 있습니다. 페이지가 로드되었는지 확인하기 위해 제목이 있는지 확인하려고 합니다. 그리고 "Join Toptal" 버튼에 대한 WebElement 필드도 필요합니다.
package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperPortalPage { private WebDriver driver; @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") private WebElement heading; @FindBy(linkText = "JOIN TOPTAL") private WebElement joinToptalButton; //Constructor public DeveloperPortalPage (WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } //We will use this boolean for assertion. To check if page is opened public boolean isPageOpened(){ return heading.getText().toString().contains("Developer portal"); } public void clikOnJoin(){ joinToptalButton.click(); } }
DeveloperApplyPage 페이지 개체
마지막으로 이 프로젝트의 세 번째이자 마지막 페이지 개체에 대해 개발자 애플리케이션 양식이 포함된 페이지를 나타내는 개체를 정의합니다. 여기에서 많은 양식 필드를 처리해야 하므로 모든 양식 필드에 대해 하나의 WebElement 변수를 정의합니다. "id"로 각 필드를 찾고 해당 필드에 대한 키 입력을 시뮬레이트하는 모든 필드에 대해 특별한 setter 메서드를 정의합니다.
package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperApplyPage { private WebDriver driver; @FindBy(tagName = "h1") WebElement heading; @FindBy() WebElement developer_email; @FindBy() WebElement developer_password; @FindBy() WebElement developer_password_confirmation; @FindBy() WebElement developer_full_name; @FindBy() WebElement developer_skype; @FindBy() WebElement join_toptal_button; //Constructor public DeveloperApplyPage(WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } public void setDeveloper_email(String email){ developer_email.clear(); developer_email.sendKeys(email); } public void setDeveloper_password(String password){ developer_password.clear(); developer_password.sendKeys(password); } public void setDeveloper_password_confirmation(String password_confirmation){ developer_password_confirmation.clear(); developer_password_confirmation.sendKeys(password_confirmation); } public void setDeveloper_full_name (String fullname){ developer_full_name.clear(); developer_full_name.sendKeys(fullname); } public void setDeveloper_skype (String skype){ developer_skype.clear(); developer_skype.sendKeys(skype); } public void clickOnJoin(){ join_toptal_button.click(); } public boolean isPageOpened(){ //Assertion return heading.getText().toString().contains("Apply to join our network as a developer"); } }
간단한 셀레늄 테스트 작성하기
페이지를 나타내는 Page Object 클래스와 해당 메서드로 사용자 상호 작용을 사용하여 이제 간단한 테스트 루틴을 일련의 간단한 메서드 호출 및 어설션으로 작성할 수 있습니다.
package com.toptal.tests; import com.toptal.webpages.DeveloperApplyPage; import com.toptal.webpages.DeveloperPortalPage; import com.toptal.webpages.HomePage; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import java.net.URL; import java.util.concurrent.TimeUnit; public class ApplyAsDeveloperTest { WebDriver driver; @Before public void setup(){ //use FF Driver driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); } @Test public void applyAsDeveloper() { //Create object of HomePage Class HomePage home = new HomePage(driver); home.clickOnDeveloperApplyButton(); //Create object of DeveloperPortalPage DeveloperPortalPage devportal= new DeveloperPortalPage(driver); //Check if page is opened Assert.assertTrue(devportal.isPageOpened()); //Click on Join Toptal devportal.clikOnJoin(); //Create object of DeveloperApplyPage DeveloperApplyPage applyPage =new DeveloperApplyPage(driver); //Check if page is opened Assert.assertTrue(applyPage.isPageOpened()); //Fill up data applyPage.setDeveloper_email("[email protected]"); applyPage.setDeveloper_full_name("Dejan Zivanovic Automated Test"); applyPage.setDeveloper_password("password123"); applyPage.setDeveloper_password_confirmation("password123"); applyPage.setDeveloper_skype("automated_test_skype"); //Click on join //applyPage.clickOnJoin(); } @After public void close(){ driver.close(); } }
테스트 실행
이 시점에서 프로젝트 구조는 다음과 같아야 합니다.
테스트를 실행하려면 트리에서 "ApplyAsDeveloperTest"를 선택하고 마우스 오른쪽 버튼을 클릭한 다음 'ApplyAsDeveloperTest' 실행 을 선택합니다.
테스트가 실행되면 IDE의 왼쪽 하단 모서리에서 결과를 볼 수 있습니다.
결론
Page Object 및 Page Factory를 사용하면 Selenium에서 웹 페이지를 쉽게 모델링하고 자동으로 테스트할 수 있으며 개발자와 QA 모두의 삶을 훨씬 더 단순하게 만들 수 있습니다. 올바르게 수행되면 이러한 Page Object 클래스를 전체 테스트 제품군에서 재사용할 수 있으며 민첩한 개발을 손상시키지 않고 초기에 프로젝트에 대한 자동화된 Selenium 테스트를 구현할 수 있습니다. 페이지 개체 모델에서 사용자 상호 작용을 추상화하고 테스트 루틴을 가볍고 단순하게 유지함으로써 적은 노력으로 테스트 스위트를 변화하는 요구 사항에 맞게 조정할 수 있습니다.
유지 관리하기 쉬운 훌륭하고 깔끔한 테스트 코드를 작성하는 방법을 보여 주었기를 바랍니다. 내가 좋아하는 QA 인용문으로 기사를 마무리하겠습니다.
두 번 생각하고 한 번 코딩하십시오!