PageObject(PO)设计模式在 UI 自动化中的实践总结(以 QQ 邮箱登陆为例)

59 阅读7分钟

PO的思想最早是2013年由IT大佬Martin Flower提出的: martinfowler.com/bliki/PageO… 没错,就是他 --- 没错,就是他 ---

在他的文章里有这样一张经典样图,图片中展示了测试代码中直接操作HTML元素和使用PO模式将page对象封装成一个HTML页面,通过特定方法来操作元素的对比;如下图:

我们知道,PO主要就是应用在UI自动化测试上(Web端和App端均适用),因此2015年,Selenium官方给出了PO的设计原则说明:github.com/SeleniumHQ/…

对官方的原则进行解读,我们可以得到如下的信息:

  • 用公共方法代表UI所提供的功能

  • 如企业微信的通讯录页面,其中有“添加成员”、“批量导入,导出”、“设置所在部门”、“删除”等功能,这些功能都可以封装成通讯录这个UI界面所提供的方法;当然,部分数据较多或者较为复杂,复用性也比较高的话,例如添加成员,也可以单独抽离出来做一个page。

  • 方法应该返回其他的PageObject或者返回用于断言的数据

  • 我们既然以页面为对象进行业务操作,那么一个方法结束后必然要有返回值:

  • 要么返回一个页面,这个页面可以是当前页(因为可能还要在这个页面进行其他操作),可以是其他页面(我们操作某个方法后很可能会跳转到另一个页面进行下一步操作);

  • 要么返回需要断言的值,测试用例总归有预期结果的对吧,那么最后肯定要有方法返回一个值,用来给我们做断言,来判断用例执行是否符合预期结果。

不要返回null或者写一个void没有返回值的方法,这样的方法没有意义,既不能为下一步操作创造条件,也不能为用例的断言提供结果。

  • 同样的行为不同的结果可以建模为不同的方法

  • 这个就比较好理解了,拿最简答的登录场景来说:

  • 同样的行为: 无论输入的账号密码正确与否,都是按照输入账号密码,点击登录这样的行为去操作

  • 不同的结果:账号密码错误和正确得到的登录响应一定是不同的。

  • 建模为不同的方法:对于登录页来说,就可以根据登录信息正确与否建模出正确登录、账号错误登录、密码错误登录等方法了

  • 不要在方法内加断言

  • 对一个测试用例的执行结果进行判断一定是在测试用例里的,方法只是提供给我们业务上需要的操作,因此断言不要加在方法里,而是应该写在用例里

    • 不要暴露页面内部的元素给外部
  • 我们使用PO的目的就是为了提高测试用例的可读性和可维护性,只要我们人能操作的事,通过page对象封装好的客户端都可以做到;就类似于一个接口,我们只关心请求操作后接口的返回值是什么,而不需要关心接口内部到底是如何工作的

    • 不需要建模UI内的所有元素
  • 一个UI页面可能会包含很多的元素,但是我们只要根据实际业务需求,将我们用的上的元素进行建模即可

  • 以页面为单位独立建模

    • 隐藏实现细节
    • 本质是面向接口编程
  • page :完成对页面的封装

    • driver :完成对Web、Android、Ios、接口的驱动
    • testcase :调用各类page完成业务流程并进行断言
    • data :配置文件和数据驱动
    • utils :其他便捷的功能封装(可选)

1.3.3 PO的优点

  • 减少例如find click这类样板代码的重复
    • 测试用例的可读性提高,只关心业务流程
    • 测试用例可维护性提高,UI页面频繁被修改了,我们只需要去修改对应PO即可,用例无需修改

说的再多,不如动手,下面以QQ邮箱登录为例,演示PO模式在UI自动化中的应用

2.1 登录场景预设 登录页面提供login功能——LoginPage类+login方法 登录页面内有多少元素并不关心,隐藏内部细节 登录成功和失败会返回不同的页面 loginSuccess——MainPage(进入主页面) loginFail——LoginPage(停留在登录页) 通过方法返回值判断登录是否符合预期

1)创建基础类BasePage,初始化driver,并封装常用的元素操作方法,如click、sendKeys等

package poshow.page;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

import java.util.List;

public class BasePage {

    public static WebDriver driver;
    
    
        public WebElement findElement(By by){
                return driver.findElement(by);
                    }
                    
                        public List<WebElement> finElements(By by){
                                return driver.findElements(by);
                                    }
                                    
                                        public void click(By by){
                                               findElement(by).click();
                                                   }
                                                   
                                                       public void sendKeys(By by,String context){
                                                               findElement(by).sendKeys(context);
                                                                   }
                                                                   
                                                                       public String getText(By by){
                                                                               return findElement(by).getText();
                                                                                   }
                                                                                   }
                                                                                   
                                                                                   ```
                                                                                   
                                                                                   
                                                                                   2)创建MainPage类,用于登录成功后的返回页面,由于这里并未演示登录后的操作,所以类中无具体方法实现,仅作为loginSuccess后的返回对象
                                                                                   
                                                                                   ```
                                                                                   package poshow.page;
                                                                                   
                                                                                   public class MainPage extends BasePage{
                                                                                   }
                                                                                   
                                                                                   ```
                                                                                   
                                                                                   
                                                                                   3)创建LoginPage类,继承BasePage类。定义所需元素定位方式并根据操作动作(输入账号、输入密码、点击登录)将其封装成具体的业务操作方法,例如登录成功,用户名错误登录、密码错误登录等,输入的测试数据作为方法的入参传入(username,password)
                                                                                   
                                                                                   ```
                                                                                   package poshow.page;
                                                                                   
                                                                                   import org.openqa.selenium.By;
                                                                                   import org.openqa.selenium.chrome.ChromeDriver;
                                                                                   import java.util.concurrent.TimeUnit;
                                                                                   
                                                                                   public class LoginPage extends BasePage{
                                                                                       //定位器
                                                                                           By usernameInput = By.name("u");  //获取用户名输入框
                                                                                               By passwordInput = By.id("p");    //获取密码输入框
                                                                                                   By submitLogin = By.cssSelector("#login_button"); //获取登录按钮
                                                                                                       By ErrM = By.id("err_m");  //获取错误提示信息
                                                                                                       
                                                                                                       
                                                                                                           public void openUrl(){
                                                                                                                   String url = "https://mail.qq.com/";
                                                                                                                           driver = new ChromeDriver();
                                                                                                                                   driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
                                                                                                                                           driver.get(url);
                                                                                                                                                   driver.manage().window().maximize();
                                                                                                                                                           driver.switchTo().frame("login_frame");
                                                                                                                                                           
                                                                                                                                                               }
                                                                                                                                                               
                                                                                                                                                                   private void sleepWait(){
                                                                                                                                                                           try {
                                                                                                                                                                                       Thread.sleep(500);
                                                                                                                                                                                               } catch (InterruptedException e) {
                                                                                                                                                                                                           e.printStackTrace();
                                                                                                                                                                                                                   }
                                                                                                                                                                                                                       }
                                                                                                                                                                                                                       
                                                                                                                                                                                                                           //业务方法
                                                                                                                                                                                                                           
                                                                                                                                                                                                                               /*
                                                                                                                                                                                                                                   登录方法
                                                                                                                                                                                                                                        */
                                                                                                                                                                                                                                            private void login(String username,String password){
                                                                                                                                                                                                                                                    findElement(usernameInput).clear();
                                                                                                                                                                                                                                                            findElement(passwordInput).clear();
                                                                                                                                                                                                                                                                    sendKeys(usernameInput,username);
                                                                                                                                                                                                                                                                            sendKeys(passwordInput,password);
                                                                                                                                                                                                                                                                                    click(submitLogin);
                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                            /*
                                                                                                                                                                                                                                                                                                  成功登录
                                                                                                                                                                                                                                                                                                       */
                                                                                                                                                                                                                                                                                                           public MainPage loginSuccess(String username,String password){
                                                                                                                                                                                                                                                                                                                   login(username,password);
                                                                                                                                                                                                                                                                                                                           return new MainPage();
                                                                                                                                                                                                                                                                                                                               }
                                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                                                   /*
                                                                                                                                                                                                                                                                                                                                        密码错误登录
                                                                                                                                                                                                                                                                                                                                             message:你输入的帐号或密码不正确,请重新输入。
                                                                                                                                                                                                                                                                                                                                                  */
                                                                                                                                                                                                                                                                                                                                                      public String loginWithErrPassword(String username,String password ){
                                                                                                                                                                                                                                                                                                                                                              login(username,password);
                                                                                                                                                                                                                                                                                                                                                                      sleepWait();
                                                                                                                                                                                                                                                                                                                                                                              return getText(ErrM);
                                                                                                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                                                                                                      /*
                                                                                                                                                                                                                                                                                                                                                                                          账号为空登录
                                                                                                                                                                                                                                                                                                                                                                                              你还没有输入帐号!
                                                                                                                                                                                                                                                                                                                                                                                                   */
                                                                                                                                                                                                                                                                                                                                                                                                       public String loginWithErrUsername(String username,String password){
                                                                                                                                                                                                                                                                                                                                                                                                               login(username,password);
                                                                                                                                                                                                                                                                                                                                                                                                                       sleepWait();
                                                                                                                                                                                                                                                                                                                                                                                                                               return getText(ErrM);
                                                                                                                                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                       /*
                                                                                                                                                                                                                                                                                                                                                                                                                                           密码为空登录
                                                                                                                                                                                                                                                                                                                                                                                                                                                */
                                                                                                                                                                                                                                                                                                                                                                                                                                                    public String loginWithoutPassword(String username,String password){
                                                                                                                                                                                                                                                                                                                                                                                                                                                            login(username,password);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                    sleepWait();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            return getText(ErrM);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                ```
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                4)最后创建LoginTest测试类,编写测试用例;用例的编写更接近于人的行为,人想要登录邮箱,只需要依靠用户名和密码完成登录的行为即可,无需关注具体的输入框和登录按钮是如何定位,如何进行输入点击的。并在用例中加入断言进行判断。
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                ```
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                package poshow.testcase;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                import org.junit.jupiter.api.*;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                import poshow.page.LoginPage;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                import static org.hamcrest.CoreMatchers.equalTo;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                import static org.hamcrest.MatcherAssert.assertThat;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                public class LoginTest {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    LoginPage loginPage = new LoginPage();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       @BeforeAll
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          static void openUrl(){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  new LoginPage().openUrl();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          @Test
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              @DisplayName("密码错误登录")
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  @Order(1)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      void loginWithErrPassword(){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              String username = "376057520";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      String password = "123456";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              String expectedErrM = "你输入的帐号或密码不正确,请重新输入。";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      String errM = loginPage.loginWithErrPassword(username, password);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              assertThat(errM,equalTo(expectedErrM));
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      @Test
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          @DisplayName("账号错误登录")
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              @Order(2)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  void loginWithErrUsername(){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          String username = "111";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  String password = "123456";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          String expectedErrM = "请输入正确的帐号!";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  String errM = loginPage.loginWithErrUsername(username, password);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          assertThat(errM,equalTo(expectedErrM));
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  @Test
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      @DisplayName("空密码登录")
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          @Order(3)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              void loginWithoutPassword(){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      String username = "376057520";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              String password = "";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      String expectedErrM = "你还没有输入密码!";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              String errM = loginPage.loginWithoutPassword(username, password);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      assertThat(errM,equalTo(expectedErrM));
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              @Test
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  @DisplayName("正确登录")
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      @Order(4)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          void logSuccess(){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 String username = "376057520";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        String password = "xxx";
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               loginPage.loginSuccess(username,password);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   ```
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    5)整体结构展示:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     - case尽量保持独立
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     - suite体系管理用例的顺序
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     - 不要把大量的业务校验逻辑放到UI自动化测试里, UI主要校验的是用户交付,操作流程,样式、数据、兼容性。
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     - 与接口测试合理的分工 #### 3.2 补充说明 以上仅仅是为了演示PO而举的一个简单的demo,实际上还有很大的优化空间:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     - 常用元素操作方法可以进一步封装的更完善
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     - 可封装常用的操作util类,例如滑动
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     - 特定元素的等待采用显示等待
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     - 登录用例可以利用参数化来以数据驱动的方式完成,使用例代码更简洁易懂
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     - PO代码和testcase代码可以分开,test下只放case代码
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     [原文链接](https://mp.weixin.qq.com/s?__biz=MzU3NDM4ODEzMg==&mid=2247487069&idx=1&sn=313d4ad27dbd5c281e6b223609801d4d&chksm=fd326c96ca45e58044922d8bbad3074ae05eed82b7d7f4bfb25cb15cc87f3b4c7c49ef3638bf#rd) 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     [更多技术文章](https://qrcode.ceba.ceshiren.com/link?name=article&project_id=qrcode&from=juejin&timestamp=1663233229&author=ML