jck28 - 小柒 - selenium - PageObject设计模式实战演练

一,产品分析

二,测试用例分析

  • 下记是商品类目创建用例,仅做参考用
    image

  • 代码实现设计思路:

    • 登录页面,首页可以视为一个类
    • 用户登陆登步骤设计为对应的方法
    • 箭头所指的方向表现为方法的返回值

三, PageObjectModel简介

1,页面对象模型

2,PO模式设计原则

  • 不要暴露页面内部的元素给外部
  • 不需要建模 UI 内的所有元素
  • 要用公共方法代表 UI 所提供的功能
  • 同样的行为不同的结果可以建模为不同的方法
  • 方法应该返回其他的 PageObject ,或者返回用于断言的数据
  • 不要在方法内加断言

3,PO模式改造

image

四,PO模式代码改造示例

1,新建BasePage类,封装公共方法

package com.hogwarts.page;

import org.openqa.selenium.By;
import org.openqa.selenium.ElementClickInterceptedException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.FluentWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.List;

public class BasePage {
    public static WebDriver driver;
    public Logger log = LoggerFactory.getLogger(BasePage.class);

    public BasePage(WebDriver webDriver) {
        driver = webDriver;
    }

    public BasePage(String baseUrl) {
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--remote-allow-origins=*");
        driver = new ChromeDriver(options);
        driver.get(baseUrl);
        driver.manage().window().maximize();
    }

    public WebElement find(By by){
        log.debug("查找的定位元素为=> " + by);
        return driver.findElement(by);
    }

    public List<WebElement> finds(By by){
        log.debug("查找的定位元素为=> " + by);
        return driver.findElements(by);
    }

    public FluentWait<WebDriver> getFluentWait(){
        FluentWait<WebDriver> fluentWait = new FluentWait(driver)
                .withTimeout(Duration.ofSeconds(10))
                .pollingEvery(Duration.ofSeconds(2))
                .ignoring(ElementClickInterceptedException.class);
        return fluentWait;
    }

    public void getUrl(String url){
        log.debug("跳转页面到=> " + url);
        driver.get(url);
    }

    public void deleteProduct(String productName){
        find(By.xpath("//*[text()='"+productName+"']//..//..//*[text()='删除']")).click();
    }

    public void quitDriver(){
        driver.quit();
    }
}

2,封装页面元素,实现页面方法

(1)登陆页面LoginPage ,返回首页页面

package com.hogwarts.page;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;

//登陆页面
public class LoginPage extends BasePage {
    //PO设计六原则 - 不要暴露页面内部的元素给外部(适用于元素定位没有统一维护且复用)
    public final By usernameBy = By.name("username");
    public final By passwordBy = By.name("username");
    public final By buttonBy = By.cssSelector("button");

    public LoginPage(WebDriver webDriver) {
        super(webDriver);
    }
    public LoginPage(String baseUrl){
        super(baseUrl);
    }

    public MainPage login(String username, String password) {
        getFluentWait().until(ExpectedConditions.elementToBeClickable(usernameBy)).clear();
//        find(usernameBy).clear();
        find(usernameBy).sendKeys(username);
        find(passwordBy).clear();
        find(passwordBy).sendKeys(password);
        find(buttonBy).click();
        // po设计六大原则之二:方法应该返回其他的 PageObject ,或者返回用于断言的数据
        return new MainPage(driver);
    }
}

(2)进入首页MainPage,通过方法进入商品列表页

package com.hogwarts.page;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;

//首页
public class MainPage extends BasePage {
    public final By productManageBy = By.xpath("//*[text()='商品管理']");
    public final By productListBy = By.xpath("//li/*[text()='商品列表']");

    public MainPage(WebDriver webDriver) {
        super(webDriver);
    }

    public ProductPage toProductPage() {
        getFluentWait().until(ExpectedConditions.elementToBeClickable(productManageBy)).click();
//        find(productManageBy).click();
        getFluentWait().until(ExpectedConditions.elementToBeClickable(productListBy)).click();
//        find(productListBy).click();
        return new ProductPage(driver);
    }
}

(3)商品列表页ProductPage ,点击添加按钮进入商品上架页面

package com.hogwarts.page;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.util.List;

public class ProductPage extends BasePage {
    public final By addButtonBy = By.xpath("//*[text()='添加']");

    public ProductPage(WebDriver webDriver) {
        super(webDriver);
    }

    public AddProductPage toAddProductPage(){
//        getFluentWait().until(ExpectedConditions.elementToBeClickable(addButtonBy)).click();
        find(addButtonBy).click();
        return new AddProductPage(driver);
    }

    public List<WebElement> getResult(String productName){
        List<WebElement> elements = finds(By.xpath("//*[text()='"+productName+"']"));
        System.out.println(elements.size());
        return elements;
    }
}

(4)商品上架页面AddProductPage ,上架商品成功后,返回商品列表页

package com.hogwarts.page;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;

public class AddProductPage extends BasePage {
    public final By productCatBy = By.cssSelector(".el-form-item is-error,.is-required:nth-child(1) input");
    public final By productNameBy = By.cssSelector(".el-form-item is-error,.is-required:nth-child(2) input");
    public final By addButtonBy = By.cssSelector(".app-container>.op-container button:nth-child(2)");

    public AddProductPage(WebDriver webDriver) {
        super(webDriver);
    }

    public ProductPage addProduct(String productCategory, String productName) {
        find(productCatBy).sendKeys(productCategory);
        find(productNameBy).sendKeys(productName);
        getFluentWait().until(ExpectedConditions.elementToBeClickable(addButtonBy)).click();
//        find(addButtonBy).click();

        return new ProductPage(driver);
    }
}

(5)新建测试用例

package com.hogwarts.page;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.openqa.selenium.WebElement;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;

class AddProductPageTest {
    public static LoginPage loginPage;
    public static MainPage mainPage;

    @BeforeAll
    static void setUpBeforeClass() {
        loginPage = new LoginPage("https://litemall.hogwarts.ceshiren.com/#/login");
        mainPage = loginPage.login("manage", "manage123");
    }

    @AfterAll
    static void tearDownAfterClass() throws InterruptedException {
        Thread.sleep(3000);
        loginPage.quitDriver();
    }

    //保证在每条用例执行完成之后都会跳转到首页
    @AfterEach
    void tearDown() throws InterruptedException {
        mainPage.getUrl("https://litemall.hogwarts.ceshiren.com/#/dashboard");
        Thread.sleep(3000);
    }

    @ParameterizedTest
    @ValueSource(strings = {"651352343","凯喜雅"})
    void addProduct(String productCategory, String productName) {
        ProductPage productPage = mainPage.toProductPage()
                .toAddProductPage()
                .addProduct(productCategory, productName);
        //获取新增数据的结果集
        List<WebElement> result = productPage.getResult(productName);
        assertEquals(1, result.size());
        //在商品列表页面删除商品信息
        productPage.deleteProduct(productName);

    }
}