• @squallff 应该没什么创新点。

    本人水平比较有限,还请高手指点啊!

    原来想做的简单一点,尽可能简单,稳定一点。
    现在来看稳定性也只有 75%。(超时,等待,或者业务逻辑)

    1. 元素采用了延迟初始化。
    2. 通用的等待方法。
    3. 封装页面。
    4. 封装控件。
    5. 回调函数。
    6. 通用类库。
    7. 数据转换及暂存。

    下面是一个简单的代码片段。

    public class AItemControl : BaseControl
    {
        private Lazy<IWebElement> _headElement;
        private Lazy<IWebElement> _test1Element;
        private Lazy<IWebElement> _test1UlElement;
        private Lazy<IWebElement> _test1Element;
        private Lazy<IWebElement> _test1Element;
    
        private By _headElemBy;
        private By _test1ElemBy;
        private By _test1UlElemBy;
        private By _test1ElemBy;
        private By _test1ElemBy;
    
        public AItemControl(IWebDriver driver)
            : base(driver)
        {
            InitBy();
            InitLazy();
        }
    
        private void InitBy()
        {
            _headElemBy    = By.ClassName("");
            _test1Element  = By.XPath("");
            _test1UlElemBy = By.Id("");
            _test1ElemBy   = By.Id("");
            _test1ElemBy   = By.Id("");
        }
    
        private void InitLazy()
        {
            LazyInitialization(out _headElement  , _headElemBy);
            LazyInitialization(out _test1Element , _test1Element);
            LazyInitialization(out _test1UlElemBy, _test1UlElemBy);
            LazyInitialization(out _test1ElemBy  , _test1ElemBy);
            LazyInitialization(out _test1ElemBy  , _test1ElemBy);
        }
    
        public bool IsSelected()
        {
            IWebElement headDiv = _headElement.Value;
    
            IWebElement bodyDiv = headDiv.FindElement(
                By.CssSelector(""));
    
            return bodyDiv.Displayed;
        }
    
        public void Select()
        {
            if (!IsSelected())
            {
                Misc.Action_LazyClick(_headElement);
            }
        }
    
        public string GetTicketCity()
        {
            return Misc.Action_LazyGetText(_test1Element);
        }
    
        public IEnumerable<string> GetAddresses()
        {
            IWebElement ul = _addressUlElement.Value;
    
            foreach (IWebElement e in ul.FindElements(By.TagName("li")))
            {
                yield return e.Text;
            }
        }
    }
    
  • @luis 模拟人的操作要走逻辑的吧。更省力的可以直接输入文本。JS 这种看情况,不能一概而论,看场景,看测试目的。每次迭代回归,保不齐哪里出错。第三方控件也有 Bug,也要集成子系统里,而且控件的显示方式也有差异,展示上是程序员写的,逻辑上我会看一看,可以研究一下各种网站的 Date 控件。具体我不敢说是否适合每个团队的需要,关键是看场景。

  • 规则可以置顶

  • 看看你想要验证什么

    1. HTTP 请求、响应?URL? fiddler 抓一下
    2. 页面? 如果有 url 跳转,可以判断 url。 如果没有,判断一个元素 style:display。
    WebDriverWait waiter  = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
    
    IWebElement e = null;
    wait.Until<bool>(
        (d) =>
        {
            try
            {
                e = driver.FindElement(By.Id(""));
            }
            catch(NoSuchElementException ex)
            {
            }
    
            return e != null && e.Enabled && e.Displayed;
        }
    
  • 给楼主 32 个赞,很好的 idea。
    有空重写一个 C# 或 Java 版的。_^

  • 那是因为需要执行 Server-Client Call,再 call 浏览器抽象层,再 call 浏览器 dom,当然很慢了。
    能说一下你使用的场景么?
    查询多少个元素?都干些啥?

  • 我看了一下 WebDriver 的 FluentWait。
    简单而言,在制定时间内根据制定条件执行你需要的代码,知道代码为 true 或已超时。
    你的代码翻译起来,就是 5 秒钟内找到 locator 的元素, 如果找到返回 true,不再查找;如果没找到,继续找,5 秒后抛出超时异常。

    你要确定一下一些内容,进行排查

    1. App 的类型, Native?Hybrid?HTML5?(我猜测是 Hybrid 的,混合应用) 2.超时时间是否够用?
    2. 是否元素的显示是通过 isDisplay() 判断的,有的元素可能 isDisplay 为 false,但是仍然显示,我遇到过这个问题。
    3. locator 传递的是否正确?这也很正常,很容易马虎的。
    4. 程序的 bug?

    附上 webDriverWait 的代码,很容易看懂的。

    /**
     * Repeatedly applies this instance's input value to the given function until one of the following
     * occurs:
     * <ol>
     * <li>the function returns neither null nor false,</li>
     * <li>the function throws an unignored exception,</li>
     * <li>the timeout expires,
     * <li>
     * <li>the current thread is interrupted</li>
     * </ol>
     *
     * @param isTrue the parameter to pass to the {@link ExpectedCondition}
     * @param <V> The function's expected return type.
     * @return The functions' return value if the function returned something different
     *         from null or false before the timeout expired.
     * @throws TimeoutException If the timeout expires.
     */
    public <V> V until(Function<? super T, V> isTrue) {
      long end = clock.laterBy(timeout.in(MILLISECONDS));
      Throwable lastException = null;
      while (true) {
        try {
          V value = isTrue.apply(input);
          if (value != null && Boolean.class.equals(value.getClass())) {
            if (Boolean.TRUE.equals(value)) {
              return value;
            }
          } else if (value != null) {
            return value;
          }
        } catch (Throwable e) {
          lastException = propagateIfNotIngored(e);
        }
    
        // Check the timeout after evaluating the function to ensure conditions
        // with a zero timeout can succeed.
        if (!clock.isNowBefore(end)) {
          String toAppend = message == null ?
              " waiting for " + isTrue.toString() : ": " + message;
    
          String timeoutMessage = String.format("Timed out after %d seconds%s",
              timeout.in(SECONDS), toAppend);
          throw timeoutException(timeoutMessage, lastException);
        }
    
        try {
          sleeper.sleep(interval);
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
          throw new WebDriverException(e);
        }
      }
    }
    
  • UI 测试稳定性是最大的挑战,这种情况也很正常。元素没有加载,元素加载缓慢,程序 bug 跟本就没有该元素。很多原因,Native,Hybrid 或 H5 还有一定的差别。跟控件显示的时间,控件是否显示,控件是否可用有关系。另外,第一次没有找到,是否需要显示等待。我有部分代码会封装成延迟初始化的形式,页面不对其进行初始化。等到需要使用该元素的时候,才进行查找,查找使用 WebDriver 的显示等待;当然,问题也很明显,当元素明显不存在的时候,代码还是会执行一段等待时间。当然,从整体的角度来考虑已经提高了一定的稳定性。我们碰到的,多是不稳定,尤其是测试版本的软件问题相当多。自动化很神秘,用其来不容易,盲目使用不讨好,不使用又不忍。用在刀刃上吧。