Angularjs 用硒检测鳗鲡
我在堆栈ASP MVC+AngularJS上有一个SPA应用程序,我想测试UI。 目前,我正在尝试使用PhantomJS和WebKit驱动程序开发Selenium 这是一个带有单个元素的测试页面视图示例。列表项从服务器动态加载,并以角度为界Angularjs 用硒检测鳗鲡,angularjs,selenium,c#-4.0,phantomjs,end-to-end,pageobjects,Angularjs,Selenium,C# 4.0,Phantomjs,End To End,Pageobjects,我在堆栈ASP MVC+AngularJS上有一个SPA应用程序,我想测试UI。 目前,我正在尝试使用PhantomJS和WebKit驱动程序开发Selenium 这是一个带有单个元素的测试页面视图示例。列表项从服务器动态加载,并以角度为界 <div id="items"> <li>text</li> <li>text2</li> </div> 此时没有加载的元素,并且_driver.PageSource不
<div id="items">
<li>text</li>
<li>text2</li>
</div>
此时没有加载的元素,并且_driver.PageSource不包含元素
如何等待项目加载?请不要建议
Thread.Sleep()
如果您使用的是AngularJS,那么使用量角器是个好主意
如果使用量角器,则可以使用它的waitForAngular()方法,该方法将等待http请求完成。在对元素执行操作之前,等待元素显示仍然是一种很好的做法,这取决于您的语言和实现,在同步语言中可能会出现这种情况
WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id<locator>));
除了埃迪奇的建议。如果您测试AngularJS应用程序,我强烈建议您考虑量角器 量角器将帮助您解决等待问题(同步、异步)。不过,也有一些注意事项 1-您需要用javascript开发测试
2-在处理流时有一些不同的机制这将等待页面加载/jquery.ajax(如果存在)和$http调用,以及任何伴随的摘要/呈现周期,将其丢弃在实用程序函数中并等待
/*C#示例
var pageLoadWait=新的WebDriverWait(WebDriver,TimeSpan.FromSeconds(超时));
PageLoad,等等(
(司机)=>
{
返回(bool)JS.ExecuteScript(
@"*/
试一试{
如果(document.readyState!==“完成”){
return false;//尚未加载页面
}
if(window.jQuery){
if(window.jQuery.active){
返回false;
}else if(window.jQuery.ajax&&window.jQuery.ajax.active){
返回false;
}
}
如果(窗口角度){
如果(!window.qa){
//用于在加载完成后跟踪渲染周期完成
window.qa={
doneRendering:错误
};
}
//获取此应用程序的角度喷油器(如有必要,请更换滤芯)
var injector=window.angular.element('body').injector();
//用于这些检查的存储提供程序
var$rootScope=injector.get(“$rootScope”);
var$http=injector.get(“$http”);
var$timeout=injector.get(“$timeout”);
//检查是否有摘要
if($rootScope.$$phase==='$apply'| |$rootScope.$$phase==='$digest'| |$http.pendingRequests.length!==0){
window.qa.doneRendering=false;
返回false;//角度消化或加载数据
}
如果(!window.qa.doneRendering){
//设置超时以将角度渲染标记为完成
$timeout(函数(){
window.qa.doneRendering=true;
}, 0);
返回false;
}
}
返回true;
}捕获(ex){
返回false;
}
/*");
});*/
我编写了以下代码,它帮助我解决了异步竞争条件失败的问题
$window._docReady = function () {
var phase = $scope.$root.$$phase;
return $http.pendingRequests.length === 0 && phase !== '$apply' && phase !== '$digest';
}
现在在selenium PageObject模型中,您可以等待
Object result = ((RemoteWebDriver) driver).executeScript("return _docReady();");
return result == null ? false : (Boolean) result;
如果您的web应用程序确实如您所说是使用Angular创建的,那么进行端到端测试的最佳方法就是使用Angular 在内部,量角器使用自己的
waitForAngular
方法,以确保量角器自动等待Angular完成对DOM的修改
因此,在正常情况下,您永远不需要在您的测试用例中编写显式的wait
:量角器为您这样做
您可以查看教程以了解如何设置量角器
如果你想认真使用量角器,你会想采用。如果你想要一个这样的例子,可以看看我的有角度的电话猫
使用量角器,您可以使用Javascript编写测试(量角器确实基于节点),而不是使用C#,但作为回报,量角器会处理等待您的所有内容。创建一个新类,让您可以确定使用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());
}
};
}
}
对于包含iFrame并使用AnglularJS开发的HTML页面的特殊问题,以下技巧为我节省了大量时间: 在DOM中,我清楚地看到有一个iframe包装了所有内容。 所以下面的代码应该可以工作:
driver.switchTo().frame(0);
waitUntilVisibleByXPath("//h2[contains(text(), 'Creative chooser')]");
但它不起作用,并告诉我类似“无法切换到框架。窗口已关闭”。
然后我将代码修改为:
driver.switchTo().defaultContent();
driver.switchTo().frame(0);
waitUntilVisibleByXPath("//h2[contains(text(), 'Creative chooser')]");
这之后一切都很顺利。
很明显,Angular在iframes中弄乱了一些东西,在加载页面之后,当您希望驱动程序聚焦于默认内容时,Angular frame已经移除了一些驱动程序。
希望这能对你们中的一些人有所帮助。你们可以从量角器中找到有用的代码片段。此函数将一直阻止,直到完成页面渲染。这是沙赫扎伊布·萨利姆答案的变体,只是他正在投票,我正在设置回调
def wait_for_angular(self, selenium):
self.selenium.set_script_timeout(10)
self.selenium.execute_async_script("""
callback = arguments[arguments.length - 1];
angular.element('html').injector().get('$browser').notifyWhenNoOutstandingRequests(callback);""")
将ng应用程序中的任何元素替换为“html”
如果您不想将整个开关切换到量角器,但您确实想等待我推荐使用的(Java)Angular,那么它来自于。它是基于量角器的,但你不必进行切换
我通过编写一个actions类修复了这个问题,在执行操作(单击、填充、检查等)之前,我在该类中等待Angular(使用ngWebDriver的waitForAngularRequestsToFinish())
有关代码片段,请参见我们遇到了一个类似的问题,我们的内部框架被用于测试多个站点,其中一些使用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;
}
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;
}
然后我们检查JQuery是否正确
def wait_for_angular(self, selenium):
self.selenium.set_script_timeout(10)
self.selenium.execute_async_script("""
callback = arguments[arguments.length - 1];
angular.element('html').injector().get('$browser').notifyWhenNoOutstandingRequests(callback);""")
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;
}
public bool IsJqueryBeingUsed(IJavaScriptExecutor jsExecutor)
{
var isTheSiteUsingJQuery = jsExecutor.ExecuteScript("return window.jQuery != undefined");
return (bool)isTheSiteUsingJQuery;
}
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;
}
public bool AngularIsBeingUsed(IJavaScriptExecutor jsExecutor)
{
string UsingAngular = @"if (window.angular){
return true;
}";
var isTheSiteUsingAngular = jsExecutor.ExecuteScript(UsingAngular);
return (bool) isTheSiteUsingAngular;
}
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;
}
var jquery = !IsJqueryBeingUsed(javascript) || wait.Until(x => JQueryHasLoaded(javascript));
var angular = !AngularIsBeingUsed(javascript) || wait.Until(x => AngularHasLoaded(javascript));
// Wait for Angular to Finish
function angularReady(): any {
return $browser.executeScript("return (window.angular !== undefined) && (angular.element(document).injector() !== undefined) && (angular.element(document).injector().get('$http').pendingRequests.length === 0)")
.then(function(angularIsReady) {
return angularIsReady === true;
});
}
$browser.wait(angularReady, 5000).then(...);
public void PageCallingUtility()
{
if (DomHasLoaded() == true)
{
if (IsJqueryBeingUsed() == true)
{
JqueryHasLoaded();
}
if (AngularIsBeingUsed() == true)
{
AngularHasLoaded();
}
}
}