【问题标题】:Waiting for render events with Selenium and React使用 Selenium 和 React 等待渲染事件
【发布时间】:2019-09-28 01:18:44
【问题描述】:

我开始使用 Selenium 来测试我构建的 React 应用程序。

在大多数简单的 JavaScript 应用程序中,连接一个测试是微不足道的,其中使用 WebDriverWait 类等待某个事件表明测试可以继续下一步。

例如:

using (IWebDriver driver = new ChromeDriver())
{
    driver.Navigate().GoToUrl($"{Properties.Settings.Default.ApplicationUrl}/Dashboard");

    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    wait.Until(d => d.Title.StartsWith("dashboard", StringComparison.OrdinalIgnoreCase));

    //Can do next thing....

    Assert.IsTrue(true);
}

但是,如果我们想要测试一个 React 应用程序,事情会变得有点复杂。鉴于 React 应用程序中的每个组件将各自单独调用their own lifecycle events,因此很难知道给定控件何时完成其Render() 生命周期。

我玩过一个简单的解决方案,例如:

//Add something like this to each React component
componentDidMount() {
    if (!window.document.hasMyComponentMounted)
        window.document.hasMyComponentMounted = true;
}

然后我可以在我的测试中做这样的事情:

using (IWebDriver driver = new ChromeDriver())
{
    IJavaScriptExecutor js = (IJavaScriptExecutor)driver;

    driver.Navigate().GoToUrl($"{Properties.Settings.Default.ApplicationUrl}/Dashboard");

    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    wait.Until(d => js.ExecuteScript("return window.document.hasHomeMounted"));

    //Can do next thing....

    Assert.IsTrue(true);
}

但这似乎是一个糟糕的解决方案。

有没有人遇到过同样的问题或知道潜在的解决方法(除了只使用Thread.Sleep())?

【问题讨论】:

  • 一般来说,您只需等待某些条件成立。如果事件从不更新 DOM,那么你就必须像你在这里做的那样做。如果在您的超时期限内未满足条件,WebDriverWait 将引发超时,如果发现项目但由于尚未完成的客户端循环而正在更改,则将引发陈旧元素异常。 (在这种情况下再次调用......)
  • 我建议你使用 Cypress 而不是 Selenium。 Cypress 有一些强大的功能,其中之一是docs.cypress.io/api/commands/route.html#Syntax。唯一的缺点是你必须用 javascript、jquery 编写代码,我想这根本不是问题

标签: c# reactjs selenium selenium-webdriver


【解决方案1】:

我为 React.js 应用程序编写了大量自动化程序,正如您所提到的,等待组件呈现是一个非常困难的问题。

没有可以解决所有问题的具体解决方案。您提到的等待组件安装的解决方案是一个很好的解决方案。您可以通过使用 WebDriver 调用将其包装起来使其更简洁,以便它们自动等待组件挂载,而您不必在测试用例中指定该代码。

这是一个想法:

public static class WebDriverExtensions()
{
    public static void WaitForComponents(this IWebDriver driver)
    {
        var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
        wait.Until(d => js.ExecuteScript("return window.document.hasHomeMounted"));
    }

    public static void GoToUrl(this IWebDriver driver, string url)
    {
        driver.Navigate().GoToUrl(url);
        driver.WaitForComponents();
    }
}

然后,您的测试用例将如下所示:

using WebDriverExtensions;

using (IWebDriver driver = new ChromeDriver())
{
    IJavaScriptExecutor js = (IJavaScriptExecutor)driver;

    driver.GoToUrl($"{Properties.Settings.Default.ApplicationUrl}/Dashboard");


    //Can do next thing....

    Assert.IsTrue(true);
}

如果您正在检查的组件不断变化,即hasHomeMounted 仅适用于主页,则解决方案有点丑陋。您可以尝试将组件名称作为参数来检查它是否已安装:

    public static void WaitForComponent(this IWebDriver driver, string componentName)
    {
        var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
        wait.Until(d => js.ExecuteScript($"return window.document.has{componentName}Mounted"));
    }

正如我所提到的,没有万能的方法来实现这一点,并且不建议在任何地方使用Thread.Sleep()。您检查反应组件安装的解决方案是一个很好的解决方案。

如果您不喜欢上述解决方案,以下是我专门用于处理大部分 React UI 测试的解决方案:

public static void WaitAndClick(this IWebDriver driver, By by)
{
     var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
     wait.Until(ExpectedConditions.ElementToBeClickable(by));

     driver.FindElement(by).Click();
}

我在自动化 React UI 方面遇到的问题涉及加载时间过长的元素。大部分页面都会加载,但有时我尝试单击的组件不存在。所以我将Click() 包裹在WaitAndClick() 方法中,以确保在我尝试执行点击之前该元素是可点击的。这适用于我的大多数情况。

【讨论】:

  • 我只是注意到你在这个答案上付出了多少努力,因为我只是回顾了这个问题。非常感谢您提供的精彩信息!
  • 很高兴听到这仍然对您有帮助!自动化快乐!
  • 这也是我可以使用 WebDriver.IO 解决的唯一解决方案(浪费了这么多时间!)
猜你喜欢
  • 2019-03-06
  • 2016-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多