用Selenium测试AngularJS

dee*_*zen 27 selenium end-to-end c#-4.0 phantomjs angularjs

我在堆栈ASP MVC + AngularJS上有一个SPA应用程序,我想测试UI.现在我正在尝试使用PhantomJS和WebKit驱动程序的Selenium.

这是一个示例测试页面 - 具有单个元素的视图.列表项<li>从服务器动态加载,并由Angular限制.

<div id="items">
    <li>text</li>
    <li>text2</li>
</div>
Run Code Online (Sandbox Code Playgroud)

我正在尝试通过测试,并且此行中存在错误:

_driver.FindElements(By.TagName('li'))
Run Code Online (Sandbox Code Playgroud)

此时没有加载的元素,_driver.PageSource不包含元素.

我该如何等待加载项目?请不要建议Thread.Sleep()

npj*_*hns 38

这将等待页面加载/ jquery.ajax(如果存在)和$ http调用,以及任何伴随的摘要/渲染周期,将其抛入实用程序函数并等待.

/* C# Example
 var pageLoadWait = new WebDriverWait(WebDriver, TimeSpan.FromSeconds(timeout));
            pageLoadWait.Until<bool>(
                (driver) =>
                {
                    return (bool)JS.ExecuteScript(
@"*/
try {
  if (document.readyState !== 'complete') {
    return false; // Page not loaded yet
  }
  if (window.jQuery) {
    if (window.jQuery.active) {
      return false;
    } else if (window.jQuery.ajax && window.jQuery.ajax.active) {
      return false;
    }
  }
  if (window.angular) {
    if (!window.qa) {
      // Used to track the render cycle finish after loading is complete
      window.qa = {
        doneRendering: false
      };
    }
    // Get the angular injector for this app (change element if necessary)
    var injector = window.angular.element('body').injector();
    // Store providers to use for these checks
    var $rootScope = injector.get('$rootScope');
    var $http = injector.get('$http');
    var $timeout = injector.get('$timeout');
    // Check if digest
    if ($rootScope.$$phase === '$apply' || $rootScope.$$phase === '$digest' || $http.pendingRequests.length !== 0) {
      window.qa.doneRendering = false;
      return false; // Angular digesting or loading data
    }
    if (!window.qa.doneRendering) {
      // Set timeout to mark angular rendering as finished
      $timeout(function() {
        window.qa.doneRendering = true;
      }, 0);
      return false;
    }
  }
  return true;
} catch (ex) {
  return false;
}
/*");
});*/
Run Code Online (Sandbox Code Playgroud)


Sha*_*haz 26

创建一个新类,让您了解使用AngularJS的网站是否已完成AJAX调用,如下所示:

import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;

public class AdditionalConditions {
    public static ExpectedCondition<Boolean> angularHasFinishedProcessing() {
        return new ExpectedCondition<Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                return Boolean.valueOf(((JavascriptExecutor) driver).executeScript("return (window.angular !== undefined) && (angular.element(document).injector() !== undefined) && (angular.element(document).injector().get('$http').pendingRequests.length === 0)").toString());
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用以下代码在代码中的任何位置使用它:

WebDriverWait wait = new WebDriverWait(getDriver(), 15, 100);
wait.until(AdditionalConditions.angularHasFinishedProcessing()));
Run Code Online (Sandbox Code Playgroud)


小智 6

我们有一个类似的问题,我们的内部框架用于测试多个站点,其中一些使用JQuery,一些使用AngularJS(1甚至有混合!).我们的框架是用C#编写的,所以执行的任何JScript都是以最小的块完成的(为了调试目的).它实际上花了很多上面的答案并将它们混合在一起(所以信用归功于@npjohns).以下是我们所做的解释:

如果已加载HTML DOM,则以下内容返回true/false:

        public bool DomHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
    {

        var hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");
        while (hasThePageLoaded == null || ((string)hasThePageLoaded != "complete" && timeout > 0))
        {
            Thread.Sleep(100);
            timeout--;
            hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");
            if (timeout != 0) continue;
            Console.WriteLine("The page has not loaded successfully in the time provided.");
            return false;
        }
        return true;
    }
Run Code Online (Sandbox Code Playgroud)

然后我们检查是否正在使用JQuery:

public bool IsJqueryBeingUsed(IJavaScriptExecutor jsExecutor)
    {
        var isTheSiteUsingJQuery = jsExecutor.ExecuteScript("return window.jQuery != undefined");
        return (bool)isTheSiteUsingJQuery;
    }
Run Code Online (Sandbox Code Playgroud)

如果正在使用JQuery,我们检查它是否已加载:

public bool JqueryHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
        {
                var hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");
                while (hasTheJQueryLoaded == null || (!(bool) hasTheJQueryLoaded && timeout > 0))
                {
                    Thread.Sleep(100);
                timeout--;
                    hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");
                    if (timeout != 0) continue;
                    Console.WriteLine(
                        "JQuery is being used by the site but has failed to successfully load.");
                    return false;
                }
                return (bool) hasTheJQueryLoaded;
        }
Run Code Online (Sandbox Code Playgroud)

然后我们对AngularJS做同样的事情:

    public bool AngularIsBeingUsed(IJavaScriptExecutor jsExecutor)
    {
        string UsingAngular = @"if (window.angular){
        return true;
        }";            
        var isTheSiteUsingAngular = jsExecutor.ExecuteScript(UsingAngular);
        return (bool) isTheSiteUsingAngular;
    }
Run Code Online (Sandbox Code Playgroud)

如果它正在使用,那么我们检查它是否已加载:

public bool AngularHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
        {
    string HasAngularLoaded =
        @"return (window.angular !== undefined) && (angular.element(document.body).injector() !== undefined) && (angular.element(document.body).injector().get('$http').pendingRequests.length === 0)";            
    var hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);
                while (hasTheAngularLoaded == null || (!(bool)hasTheAngularLoaded && timeout > 0))
                {
                    Thread.Sleep(100);
                    timeout--;
                    hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);
                    if (timeout != 0) continue;
                    Console.WriteLine(
                        "Angular is being used by the site but has failed to successfully load.");
                    return false;

                }
                return (bool)hasTheAngularLoaded;
        }
Run Code Online (Sandbox Code Playgroud)

检查DOM是否已成功加载后,您可以使用这些bool值进行自定义等待:

    var jquery = !IsJqueryBeingUsed(javascript) || wait.Until(x => JQueryHasLoaded(javascript));
    var angular = !AngularIsBeingUsed(javascript) || wait.Until(x => AngularHasLoaded(javascript));
Run Code Online (Sandbox Code Playgroud)


edd*_*iec 3

如果您使用 AngularJS,那么使用 Protractor 是一个好主意。

如果您使用量角器,您可以使用它的 waitForAngular() 方法,该方法将等待 http 请求完成。在对元素进行操作之前等待元素显示仍然是一个很好的做法,具体取决于您的语言和实现,它可能在同步语言中看起来如此

WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id<locator>));
Run Code Online (Sandbox Code Playgroud)

或者在 JS 中你可以使用 wait 方法来执行一个函数直到它返回 true

browser.wait(function () {
    return browser.driver.isElementPresent(elementToFind);
});
Run Code Online (Sandbox Code Playgroud)