自动化工具 selenium: 死磕页面跳转,主页 - 登录页 - 主页

cai · 2017年10月11日 · 最后由 槽神 回复于 2017年10月12日 · 5955 次阅读
本帖已被设为精华帖!

最近在尝试将工作中常用到的一些配置操作从手工替换成自动化脚本,为实现这个目标我尝试了 selenium IDE 和 selenium webdriver。在这篇文章中想要分享的是登录页面跳转这个简单步骤的一些操作和思考。目标操作步骤如下图所示:

使用 selenium IDE 的实现

鉴于公认的 selenium IDE 上手容易,我首先尝试在 selenium IDE 里录制整个过程。下图是录制出来的结果,只有 4 个 command,打开主页网址,在跳转到的登录页输入用户名、密码信息,提交信息跳转显示主页。在这个过程中,以下几个问题值得思考。

  • 等待页面加载:selenium IDE 可以控制回放脚本的执行速度,如果执行速度过快,会有一些元素还没有加载出来就被下一步使用,这会导致下一步出现 “element not found” 错误。但 open 命令和 clickAndWait 命令,都是打开加载新的页面,却基本不会出现这种问题。回放速度的快慢并不会影响这两个命令。可以看一下这两个命令的说明:

    • open: waits for the page to load before proceeding, ie. the "AndWait" suffix is implicit.
    • clickAndWait: If the click action causes a new page to load (like a link usually does), call waitForPageToLoad.
    • 与此类似的还有 waitForPageToLoad、waitForText、waitForElementPresent 等命令均可以用来等待页面加载、预防出现因来不及加载完全而出现的问题。
  • 参数变量化:直接录制的结果中,用户名、密码是直接记录在 command 对应的 value 里的。如果要复用该 test case,用户名密码可能会改变,这时候就需要重新手动修改 value 值。如果一个 test case 中有多个地方需要这些值(比如,登陆之后的校验等),在修改的时候容易出现疏漏。此时需要将这些 value 值变量化,对应的命令是 store,修改后的代码如下:

  • 分支:按照最开始的流程图,打开主页网址的时候可能出现两种情况,主页或登录页。所以需要先进行校验,只有在显示登录页时才需要输入用户名密码。selenium IDE 中默认没有 if 这个命令,需要用 gotoIf 和 label 来代替,同时需要将要校验的条件变量化。

  • while 循环:流程图的最后一部分是检验用户名密码是否正确,能否顺利登陆。再添加一对 gotoIf 和 label 可实现类似 while 的功能。我在两个 gotoIf 里采用了不同形式的条件语句,可以看到它允许 ture、false 判断,也允许直接与字符串比对等。

到这里,已经用 selenium IDE 实现了流程图的所有功能。总结一下:

  • 优点:
    • Easy to program:command 形式固定,熟悉常用的几个 command 就可以完成一个基本的自动化脚本,没有复杂的语法。另外还有一些 plugin 可以应用,进一步完善功能。
    • Easy to locate:定位 web element 是 web UI 自动化中很重要的一环,selenium IDE 可以直接在浏览器页面选择 element 获取定位方法,很是方便。
    • Easy to replay:编写好的 test case 可以直接运行,随时暂停、修改。大为降低了调试脚本的成本。
  • 缺点:
    • Firefox only:从各种官方文档来看,selenium IDE 是针对 Firefox 推出的,虽然据说 Chrome、IE 浏览器也存在类似功能的插件,但毕竟不是官方主推,功能还待验证。
    • Not a recording tool:我一开始误以为这是一款录制脚本的工具,但使用后才明白 IDE 这个名字不是白叫的。录制的脚本距离可用于生产的自动化脚本还相差甚远,需要补充逻辑、变量化、添加校验等等。
    • Poor maintainability and repeating code:就以这段登录页面来说,虽然我们可以把这段脚本保存为一个 test case,然后在所有需要它的 test suite 里包含这个 test case,但这也意味着每次都要手动修改其中的常量信息,要比程序中方法调用复杂的多。

鉴于这些我问题,我决定把 selenium IDE 当做一个工具,而真正的自动化程序还是得靠 selenium webdriver。

使用 selenium webdriver 的实现

与 selenium IDE 部分相对应,首先给出实现主流程的功能代码。其中 selenium IDE 命令与 selenium webdriver 方法对应:open -- get(), type -- sendKeys(), clickAndWait -- click() + wait.until()。selenium webdriver 里定位 web element 需要借助 findElement() 方法和 By 类定义的 locator。

public class TestSelenium {
    private WebDriver webDriver;
    String homePageURL = "https://...";

    @BeforeTest
    public void setup(){
        System.setProperty("webdriver.gecko.driver","/Users/.../geckodriver");
        webDriver = new FirefoxDriver();
    }

    @Test
    public void test(){
        webDriver.get(homePageURL);
        webDriver.findElement(By.id("userid")).sendKeys("Z...");
        webDriver.findElement(By.id("password")).sendKeys("W...");
        webDriver.findElement(By.id("btnActive")).click();
        WebDriverWait wait = new WebDriverWait(webDriver,60);
        wait.until(ExpectedConditions.titleIs("O..."));
    }

    @AfterTest
    public void quitDriver(){...}
}
  • 等待页面加载:selenium webdriver 里的等待有两种,显示等待和隐式等待。代码中的 wait.until 就是显示等待的使用。根据源码注释,get 方法会等待页面加载完全才会真正结束,这就说明 get 方法之后不需要显示等待来配合。

  • 参数变量化:变量化用户名、密码,同时将登录部分代码 extract 成新的 private 方法,便于日后复用。

@Test
public void test(){
    webDriver.get(homePageURL);
    signIn("Z...", "W...");
    WebDriverWait wait = new WebDriverWait(webDriver,5);
    wait.until(ExpectedConditions.titleIs("O..."));
    //等待5s,检查网页title,确认是否已登录成功跳转
}

private void signIn(String userid, String passwd) {
    webDriver.findElement(By.id("userid")).sendKeys(userid);
    webDriver.findElement(By.id("password")).sendKeys(passwd);
    webDriver.findElement(By.id("btnActive")).click();
}

  • 分支 & while 循环:if 和 while 语句均符合我们的习惯,无二次学习成本。
@Test
public void test(){
    webDriver.get(homePageURL);

    if(!webDriver.getTitle().equalsIgnoreCase("O...")) {
    //是否需要登录
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        LocalTime localTime = LocalTime.now();
        LocalTime deadLine = localTime.plusMinutes(1);
        while(LocalTime.now().isBefore(deadLine)) {
        //限制允许登录失败重试的时间,大多数情况下应该没有必要用这个while,是我想太多
            try {
                signIn("Z...", "W..."); //如果有多个同样性质的用户作为备选登录者,这里可改进为读入数据,一个失败后,可在下个循环更换另一个用户,重试。
                wait.until(ExpectedConditions.titleIs("O..."));
                //等待5s,检查网页title,确认是否已登录成功跳转
                System.out.println(webDriver.getTitle());
                return;//如果登录成功,则进行其他操作后退出
            } catch (TimeoutException e) {
                continue;//如果登录失败,则重新登录
            }
        }
        throw new TimeoutException();//超时,仍未登录成功,抛出异常
    }
    System.out.println(webDriver.getTitle());    
}

进行到这里,其实也已经实现了流程图的全部步骤。但仍让人不满意的是 selenium webdriver 对 web element 的定位冗余、不利于复用,以及 test 包含太多逻辑。为了进一步提高代码质量,Page Object Pattern 闪亮登场。

Page Object Pattern

这个模式指的是,将每个页面看做一个对象,这些对象有自己的属性(web element 等)和方法( 信息提交、页面跳转等 action ),将这些信息放在一起,而 test 对应的方法负责想要测试的一个功能点,其内部生成对象实例、调用对象的方法、并负责校验功能是否符合预期。而 PageFactory 类则是实现 Page Object Pattern 必不可少的利器。

以我的这个登录流程为例,测试目标是输入主页网址可能产生的几种操作。这里涉及到两个页面:登录页(LoginPage)和主页(Homepage)。

  • LoginPage:登录页我们关心的元素为用户名输入框、密码输入框、登录按钮,动作为点击登录按钮提交信息。下面例子中给出的代码对应包含三个属性、一个动作的定义。其中三个属性的初始化并没有用之前提到的 “webDriver.findElement(By.id("userid"))”,而是用注解@FindBy,这正是 PageFactory 的作用。这里还有一点需要注意的是,注解在编译期将信息传递给 JVM,而变量是在运行时才有具体值,所以@FindBy注解中 using 部分按理说只能为常量不能是变量。虽然高手可能有办法绕过这一点,但我这样的菜鸟还是觉得遵守这点为好。
public class LoginPage {
    private WebDriver  webDriver;

    @FindBy(how = How.ID, using = "userid")
    private WebElement userid;

    @FindBy(how = How.ID, using = "password")
    private WebElement passwd;

    @FindBy(how = How.ID, using = "btnActive")
    private WebElement submitbtn;

    public LoginPage(WebDriver webDriver){
        this.webDriver = webDriver;
        PageFactory.initElements(webDriver,this);
        //初始化page object,传递webdriver,查找element
        //去掉这一句会产生“NullPointerException”
        //可以将这条语句移到test方法new page object部分,同时省略类定义中的webdriver属性,具体见下文代码        
    };

    public void submitLoginInfo(String username, String password) {
        userid.sendKeys(username);
        passwd.sendKeys(password);
        submitbtn.submit();
    }
  • HomePage:主页我们关心的元素主要是页面 title 或是任何可以可以用来判断当前页面是不是主页的特征元素,动作则是输入网址打开主页、验证主页特征元素。在 selenium IDE 版本里,这一点是通过验证当前页面的 title 来实现的。在下面的例子里,我用的是主页显示用户名的元素来验证成功登陆进入主页。
public class HomePage {
    WebDriver webDriver;
    private String username = "ZHRA-HRSPC1";
    private String password = "Welcome1";

    @FindBy(how = How.XPATH, using = "/html/body/div[2]/form/...")
    WebElement loggedUser;

    public HomePage(WebDriver webDriver, String... args) { 
        .... //构造函数
        if(args.length>1){
            username = args[0];
            password = args[1];
        }else if(args.length>0){
            username = args[0];
        }//传递用户名、密码,也可以写成构造函数重载
    }
    public void getHomePage(){    
        ....        
        webDriver.get(homePageUrl);        
        ....//同上文,设置WebDriverWait

                try {
                    loginPage.submitLoginInfo(username, password);
                    wait.until(ExpectedConditions.visibilityOf(loggedUser));//判断条件为loggedUser元素是否显示
                    return;
                } 
        ....

  • @Test:如下所示,真正的测试部分的代码就非常简单直观了。将登录页面跳转等自动逻辑隐藏起来,简化复用成本。
private WebDriver webDriver;
private HomePage homePage;

@Test
public void loginTest(){
    homePage.getHomePage();//new homePage的时候可以将自定义的用户名、密码作为参数传递
}

LoadableComponent

在 page object pattern 里,每个页面都可能涉及判断页面元素是否加载完成的问题。对于动态加载页面来说,这个工作量很繁琐,也不美观。selenium 提供了 LoadableComponent 基础类,作为 page object pattern 的扩展,提供更一致且优雅的解决方法。

具体方法为

  1. 声明类时继承 LoadableComponent<>类
  2. 覆写 load 方法和 isLoaded 方法
  3. 调用类对象的 get 方法访问页面

load 方法内包含的是访问页面的操作,isLoaded 方法内包含的是检查页面元素是否加载成功的操作。从源代码可以看出,get 方法首先调用 isLoaded 方法判断是否加载成功,没有成功的话会捕获 Error,然后调用 load 方法加载,之后会再次调用 isLoaded 方法判断,并返回。如果第二次加载仍未成功则会抛出 Error。

采用这种方法改写的 page 类及 test 方法。

  • LoginPage:
public class LoginPage extends LoadableComponent<LoginPage>{

    ....

    @Override
    protected void load() {}

    @Override
    protected void isLoaded() throws Error{
        try{
            new WebDriverWait(webDriver, 30).until(ExpectedConditions.visibilityOf(userid));
        }catch (WebDriverException ex){
            throw new Error("LoginPage was not successfully loaded in 30s");
        }
    }
}
  • HomePage:
public class HomePage extends LoadableComponent<HomePage> {

   ....

    @Override
    protected void load() {
        String homePageUrl = "https://...";
        webDriver.get(homePageUrl);
    }

    @Override
    protected void isLoaded() throws Error {
        if (!webDriver.getTitle().equalsIgnoreCase("O...") && !webDriver.getTitle().equalsIgnoreCase("Sign in")){
            throw new Error("neither homepage nor login page appears");
        }

        if (webDriver.getTitle().equalsIgnoreCase("Sign in")) {
            WebDriverWait wait = new WebDriverWait(webDriver, 5);
            try {
                 loginPage.get();
                 loginPage.submitLoginInfo(username,password);
                 wait.until(ExpectedConditions.visibilityOf(loggedUser));
            } catch (TimeoutException e) {
                 throw new TimeoutException("fail to login");
            }
        }

        if (!webDriver.getTitle().equalsIgnoreCase("O...")){
            throw new TimeoutException("fail to load homepage in almost 1 minute");
        }
    }
}
@Test
    public void loginTest(){
        homePage.get();
    }

未完成的改进

至此,针对一个我工作中常见的步骤 -- 登录页面跳转,使用 selenium IDE 和 webdriver 进行了实现,同时尝试应用了 page object pattern。

但我对现在的代码仍不是很满意,只是目前为止还没有找到进一步改进的方法。譬如,selenium get(url) 方法可以打开一个页面并等待其加载完成,那么通过点击按钮跳转到全新页面时,有没有类似的方法可以等待新的页面加载完成呢?

如果日后我找到更简单的实现,会继续更新的,也希望有想法的同事能为我指点迷津。

后续更新 1

上文中提到的 “通过点击按钮跳转到全新页面时,有没有类似的方法可以等待新的页面加载完成呢?” 其实在 Page object 理念里应该已经得到了解决。比如,新打开的页面是另一个 page object,将其写成 loadable component 的形式,在 isLoaded 和 load 方法中等待并判断应该就可以实现这一点。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 11 条回复 时间 点赞
cai 关闭了讨论 11月01日 15:34

单步等待的 API 封装,一般不赞成这么用

/**
   * Description: set element locate timeout.</BR> 
   * 内容描述:设置对象查找超时时间.
   *
   * @param seconds
   *            timeout in timeunit of seconds.
   */
  protected void setElementLocateTimeout(int seconds) {
      driver.manage().timeouts().implicitlyWait(seconds, TimeUnit.SECONDS);
  }

  /**
   * wait for the element visiable in timeout setting</BR> 
   * 在指定时间内等待,直到对象可见。
   *
   * @param by
   *            the element locator By
   * @param seconds
   *            timeout in seconds
   */
  protected boolean waitForElementVisible(By by, int seconds) {
      try {
          setElementLocateTimeout(seconds);
          WebDriverWait wait = new WebDriverWait(driver, seconds, stepTimeUnit);
          return wait.until(ExpectedConditions.visibilityOfElementLocated(by)) != null;
      } finally {
          setElementLocateTimeout(maxWaitfor);
      }
  }

  /**
   * wait for the element visiable in timeout setting</BR> 
   * 在指定时间内等待,直到对象可见。
   *
   * @param element
   *            the element to be found.
   * @param seconds
   *            timeout in seconds.
   */
  protected boolean waitForElementVisible(WebElement element, int seconds) {
      try {
          setElementLocateTimeout(seconds);
          WebDriverWait wait = new WebDriverWait(driver, seconds, stepTimeUnit);
          return wait.until(ExpectedConditions.visibilityOf(element)) != null;
      } finally {
          setElementLocateTimeout(maxWaitfor);
      }
  }

  /**
   * wait for the element not visiable in timeout setting</BR>
   * 在指定时间内等待,直到对象不可见。
   *
   * @param by
   *            the element locator.
   * @param seconds
   *            timeout in seconds.
   */
  protected boolean waitForElementNotVisible(By by, int seconds) {
      try {
          setElementLocateTimeout(seconds);
          WebDriverWait wait = new WebDriverWait(driver, seconds, stepTimeUnit);
          return wait.until(ExpectedConditions.invisibilityOfElementLocated(by)) != null;
      } finally {
          setElementLocateTimeout(maxWaitfor);
      }
  }

  /**
   * wait for the element present in timeout setting</BR> 
   * 在指定时间内等待,直到对象出现在页面上。
   *
   * @param by
   *            the element locator.
   * @param seconds
   *            timeout in seconds.
   */
  protected boolean waitForElementPresent(By by, int seconds) {
      try {
          setElementLocateTimeout(seconds);
          WebDriverWait wait = new WebDriverWait(driver, seconds, stepTimeUnit);
          return wait.until(ExpectedConditions.presenceOfElementLocated(by)) != null;
      } finally {
          setElementLocateTimeout(maxWaitfor);
      }
  }

  /**
   * wait for the element clickable in timeout setting</BR>
   * 在指定时间内等待,直到对象能够被点击。
   *
   * @param by
   *            the element locator By
   * @param seconds
   *            timeout in seconds
   */
  protected boolean waitForElementClickable(By by, int seconds) {
      try {
          setElementLocateTimeout(seconds);
          WebDriverWait wait = new WebDriverWait(driver, seconds, stepTimeUnit);
          return wait.until(ExpectedConditions.elementToBeClickable(by)) != null;
      } finally {
          setElementLocateTimeout(maxWaitfor);
      }
  }

  /**
   * wait for text appears on element in timeout setting</BR>
   * 在指定时间内等待,直到指定对象上出现指定文本。
   *
   * @param by
   *            the element locator By
   * @param text
   *            the text to be found of element
   * @param seconds
   *            timeout in seconds
   */
  protected boolean waitForTextOnElement(By by, String text, int seconds) {
      try {
          setElementLocateTimeout(seconds);
          WebDriverWait wait = new WebDriverWait(driver, seconds, stepTimeUnit);
          return wait.until(ExpectedConditions.textToBePresentInElementLocated(by, text)) != null;
      } finally {
          setElementLocateTimeout(maxWaitfor);
      }
  }

  /**
   * wait for text appears in element attributes in timeout setting</BR>
   * 在指定时间内等待,直到指定对象的某个属性值等于指定文本。
   *
   * @param by
   *            the element locator By
   * @param text
   *            the text to be found in element attributes
   * @param seconds
   *            timeout in seconds
   */
  protected boolean waitForTextOfElementAttr(By by, String text, int seconds) {
      try {
          setElementLocateTimeout(seconds);
          WebDriverWait wait = new WebDriverWait(driver, seconds, stepTimeUnit);
          return wait.until(ExpectedConditions.textToBePresentInElementValue(by, text)) != null;
      } finally {
          setElementLocateTimeout(maxWaitfor);
      }
  }

这是等待对象出现的方法:

/**
     * 等待对象出现
     * 
     * @param type
     *            对象类型:id,name,css,xpath,class,linktext,tagname,partialLinkText
     * @param value
     *            对象的值
     * @param iTimeout
     *            超时时间
     * @return FALSE:对象未在规定时间内出现 TRUE:对象在规定时间内出现
     */
    public boolean dynamicWaitAppear(WebElementType type, String value, Integer iTimeout) {
        try {
            switch (type) {
            case ID:
                new WebDriverWait(webDriver, Integer.valueOf(iTimeout))
                        .until(ExpectedConditions.presenceOfElementLocated(By.id(value)));
                break;
            case NAME:
                new WebDriverWait(webDriver, Integer.valueOf(iTimeout))
                        .until(ExpectedConditions.presenceOfElementLocated(By.name(value)));
                break;
            case CSS:
                new WebDriverWait(webDriver, Integer.valueOf(iTimeout))
                        .until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(value)));
                break;
            case XPATH:
                new WebDriverWait(webDriver, Integer.valueOf(iTimeout))
                        .until(ExpectedConditions.presenceOfElementLocated(By.xpath(value)));
                break;
            case CLASS:
                new WebDriverWait(webDriver, Integer.valueOf(iTimeout))
                        .until(ExpectedConditions.presenceOfElementLocated(By.className(value)));
                break;
            case LINKTEXT:
                new WebDriverWait(webDriver, Integer.valueOf(iTimeout))
                        .until(ExpectedConditions.presenceOfElementLocated(By.linkText(value)));
                break;
            case TAGNAME:
                new WebDriverWait(webDriver, Integer.valueOf(iTimeout))
                        .until(ExpectedConditions.presenceOfElementLocated(By.tagName(value)));
                break;
            case PARTIALLINKTEXT:
                new WebDriverWait(webDriver, Integer.valueOf(iTimeout))
                        .until(ExpectedConditions.presenceOfElementLocated(By.partialLinkText(value)));
                break;
            default:
                break;
            }

            String message = "等待对象" + value + "出现成功.";
            LogHelper.LogMessage(Variable.DONE, "WebManager.DynamicWaitAppear", message);
        } catch (Exception ex) {
            String screenshot = LogHelper.Capscreenshot();
            String message = "等待对象" + value + "出现失败.错误详情请见:" + screenshot;
            LogHelper.LogMessage(Variable.FAILD, "WebManager.DynamicWaitAppear", message);
            softAssert.assertFalse(true);
        }
        return true;
    }

不知道为什么要想的如此复杂,selenium 提供的方法就可以解决啊。

WebDriverWait(webDriver, Integer.valueOf(iTimeout))
                        .until(ExpectedConditions.presenceOfElementLocated(webelement(ID,xpath,name,css等等));

这个方法用于,动态等待对象出现,如果在设置的时间段内没有出现,即返回 false。
所以,你用 get(url) 之后,直接加上这个方法,判断跳转的登录页面是否出现了某元素。
贴一段我的登录代码:

/**
 * 登录
 * 
 */
public void login(String url, String email, String pwd) {
    // 打开登录网址
    webManager.NavigateTo(url);
    webManager.threadWait(3);
    // 浏览器最大化
    webManager.maximizeBroswer();
    webManager.threadWait(1);
    // 等待登录页面用户名输入框出现,60秒未出现则按超时处理
    webManager.dynamicWaitAppear(WebElementType.ID, "email", 60);
    // 输入用户名
    webManager.sendKeys(WebElementType.ID, "email", email);
    webManager.threadWait(1);
    // 输入密码
    webManager.sendKeys(WebElementType.ID, "pwd", pwd);
    webManager.threadWait(1);
    // 输入验证码
    webManager.sendKeys(WebElementType.ID, "jcaptcha", "1");
    webManager.threadWait(1);
    // 单击登录按钮
    webManager.click(WebElementType.ID, "login");
    webManager.threadWait(2);
}
cai 回复

参考一下:http://www.it1352.com/313066.html
实际操作起来,我发现其实这种操作在【效果上】还是阻塞的,并非异步
为什么呢?换个角度思考,有了这个 timeout,你在下一个 page 加载完成之前的任何操作(包括断言),也会有同样的超时设置作用于对你的 element 的加载等待,除非你故意在下一步操作之前把超时时间设置为 1s 或者更短~
所以我在脚本中基本不动这个设置,只有在初始化的时候通过全局设置一下,结合自己系统、环境的性能表现即可。

cai #8 · 2017年10月12日 Author
槽神 回复

pageLoadTimeout 这个设置之前我确实没有注意到,就我刚刚查找的相关的 selenium issues 来看,这个设置在 selenium 3 和 FF55 以下的版本存在 bug。更新到最新的 FF 和 selenium 就可以设置了。

我试了在 click 命令之前更改这个参数,确实会产生 “org.openqa.selenium.TimeoutException: Timed out waiting for page load.”。

但是,click 这个命令,官方文档里写的是 “If this causes a new page to load, you should discard all references to this element and any further operations performed on this element will throw a StaleElementReferenceException. Note that if click() is done by sending a native event (which is the default on most browsers/platforms) then the method will not wait for the next page to load and the caller should verify that themselves.”,这难道不是说它不会受设置的超时时间的限制吗?然后我就糊涂了。。。

我还是个新手,肯定还有很多理解不到的地方,请多多指教~

cai 回复

看样子你还是没好好琢磨透 webdriver 怎么工作的啊,给你个样例你看下:
每一步操作,工具都会自动等到你设置的超时时间(pageLoadTimeout、implicitlyWait)过了才会继续判定为失败,在此时间内会自动轮询检查的
对 ajax 局部刷新,一样有效

@Override
public void startWebDriver() {
    cleanBrowserProcess();
    try {
        initWebDriver();
        driver.manage().timeouts().pageLoadTimeout(maxLoadTime, TimeUnit.SECONDS);
        driver.manage().timeouts().setScriptTimeout(maxWaitfor, TimeUnit.SECONDS);
        driver.manage().timeouts().implicitlyWait(maxWaitfor, TimeUnit.SECONDS);
        driver.manage().window().maximize();
    } catch (Exception e) {
        LoggerUtils.error(e);
        throw new RuntimeException(e);
    }
}
cai #6 · 2017年10月12日 Author
纯稀饭 回复

您说的 waitForElement(elementLocator) 是指把我的代码中 wait.until(ExpectedConditions.visibilityOf(webElement)) 那部分代码封装成一个单独的方法吗?如果我理解的没错的话,这个当然可以。
只是我的问题是,是否有一个方法可以做到:点击一个 button->页面跳转到全新页面,这个新页面加载过程中程序是 block 住的,就像 get(url) 打开全新页面那样,只有等到页面加载完成才会进入下一步。这样的话下一步检查标志元素没有出现,我们就可以判断新页面打开的不对,而不用担心等待时间不够元素还没有完全加载。

waitForElement(elementLocator) 等待元素出现 500ms 扫描一次 把登陆后的一个标志元素写进去就好了

恒温 将本帖设为了精华贴 10月12日 08:09
cai #3 · 2017年10月11日 Author
槽神 回复

有道理~多谢~

点赞,书写条理很清晰,期待后续。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册