在Selenium中使用JavaScriptExecutor处理Ajax调用?

我们在这个Designing Selenium Cucumber Framework系列中走得很远我们设计了PageObjectManager来管理PageObjectsFileReaderManager来管理  ConfigFileReaderJSonFileReader以及WebDriverManager来管理WebDriver但所有这些都有助于更好的代码管理和维护。

在本章中,我们将努力从Selenium测试执行中获得更好的测试结果。如果您想构建一个成功的测试自动化解决方案,您真正需要的是使其可靠并快速实现。影响Web自动化中这两个目标的最重要因素是您在每次页面交互后如何等待。无论是单击还是将鼠标悬停在Web元素上,执行javascript函数或模拟按键操作,都可能导致页面转换或ajax操作。在执行下一个操作之前,您需要等待此操作完成。如果等待时间过长,则会导致测试执行速度慢于需要的速度。如果您不等待或等待太少,则表现为一致或间歇性故障。

我们都知道,硒测试执行有时会间歇性地进行一些测试。当您尝试修复问题并尝试调试相同的方案时,它完全正常。大部分时间都是因为元素加载延迟而发生的。意味着Selenium尝试对元素执行任何操作,但它尚未在DOM中呈现。

我知道你一定在想,那么在这种情况下,Selenium已经有一个被称为Implicit-wait的特性,它总是等待元素加载并等待指定的时间。那么为什么我们在Selenium执行中遇到这样的问题呢。答案是因为Ajax Calls或JQuery。 

 

什么是Ajax?

AJAX 是一种从网页到服务器执行XMLHttpRequest(带外Http请求)并发送/检索要在网页上使用的数据的技术。AJAX代表异步Javascript和XML

意味着: Ajax是客户端浏览器与服务器通信的方式(例如:从数据库中检索数据),而无需执行页面刷新。

什么是JQuery?

JQuery (网站)是一个javascript框架,通过构建许多可用于搜索和与DOM交互的高级功能,使得使用DOM变得更容易。jQuery的部分功能实现了一个用于执行AJAX请求的高级接口。 

  • JQuery is a lightweight client side scripting library while AJAX is a combination of technologies used to provide asynchronous data transfer
  • JQuery and AJAX are often used in conjunction with each other
  • JQuery is primarily used to modify data on the screen dynamically and it uses AJAX to retrieve data that it needs without changing the current state of the displayed page

How to check Ajax/JQuery on the Web Page?

This is very important to check that what is actually working on the application for which the tests are written. One easy way is to talk to UI Developers and understand the technology used for developing the web pages. It will always give you an fair idea what you will be dealing with on automation. Second way is to go and find by your self.

In the below example, I will use Chrome Browser and  shop.demo.com website to demonstrate the Ajax calls. To do the same on your end, please follow the steps below.

1. Go to the website shop.demoqa.com, add any product to basket/bag and reach to checkout page or the final page where you enter personal details and payment details.

2. Press F12 to open developer mode in chrome and select Console tab from the menu.

3. Now type jQuery.active in the console editor and press Enter.  

在Selenium中使用JavaScriptExecutor处理Ajax调用?Handle Ajax call Using JavaScriptExecutor in Selenium?

Note: You would see that it has returned the value 0. Zero means that at present no ajax or jquery is running on the page.

4. Lets see if any Ajax actually works on the page or not. Scroll to at the bottom of the page where there is a radio button SHIP TO A DIFFERENT ADDRESS?.

5. Select Network tab in the Chrome Developer window and click on the last drop down from the right to select the speed of the connection. Select Slow 3G.
 

阿贾克斯等待3Ajax Wait 3

Note: Select Slow 3G would reduce the connection speed and gives you enough time to check things on the page. Because sometimes Ajax calls happen at the speed of the flash.

6. Come back to Console tab and type jQuery.active, but do not press Enter. Now the next steps is to be done very swiftly, as you wont get much time to do it. You need to click on the check box and immediately come back to Console editor and hit Enter to jQuery.active statement. 

阿贾克斯等待3Ajax Wait 3

Note: You would notice value 1 this time. One means the ajax is still active on the page.

Yeah cool, isn’t it a nice way to find things. Good, now we move forward to catch Ajax calls using JavaScriptExecutor.

Handle Ajax call Using JavaScriptExecutor in Selenium?

Our test will fail tentatively due to these situations. Hence its always wise idea to wait for Ajax call to complete. This can be done using our JavaScriptExecutor interface. The idea is simple, if all the JQuery executions are completed, then it will return jQuery.active == 0 which we can use in our Wait.Until method to wait till the script return as true.

Wait for Ajax call to finish

To understand this chapter you have to learn the concepts discussed in the earlier WebDriver Waits and Smart Waits in Selenium chapter already. Also you shoud understand the Functions and Predicates in Java.

Here is a sample code to showcase the handling of AJAX controls using Selenium Webdriver. You can integrate it in your test execution class.

Boolean isJqueryCallDone = (Boolean)((JavascriptExecutor) driver).executeScript(“return jQuery.active==0“);

As the above script would return either True or False. But we need to run this code till the time either we get true or the specified time is over. To do that we need to have the Selenium WebDriver Wait, which would provide us untill method.

We will create a until method in our new Wait class which will have logic of WebDriverWait inside. This function will take WebDriver and Function<WebDriver, Boolean> as parameter. Function<WebDriver, Boolean> states that it would take driver and return bool. withTimeout will decide how long it will wait for the condition to become true.

Let’s write a complete method, which will be called from Page Objects methods and eventually  which will use the above created until method.

In the above, Until method is called, which pass driver and Function as wait condition as parameter. That wait condition is actually our Ajax query.

If it is too much to understand, try to understand from the screenshot below.

阿贾克斯等待5Ajax Wait 5

Wait for Page Load using JavaScriptExecutor in Selenium

Generally Selenium WebDriver handles software app’s page loading or wait for page to load by It self If you have used Implicit Wait In your software automation test. But many on development sites have a page loading Issues and It Is taking more or less time for page to load on every software test iteration. So some times your test will run without any Issue and some times It will be unable to found some elements due to the page loading Issue.

We can use bellow given java script syntax In our software automation test to check (loading) status of page.
document.readyState

It will return “complete” when page Is loaded completely.

This also implemented in the same way. The only change is that this one use document.readyState.

Implement Wait Utility in the Framework

1. Create a New Package and name it as selenium, by right click on the src/test/java and select New >> Package. As this some what related to Selenium WebDriverWait, I like to call it as selenium package.

2. Create a New Class and name it as Wait by right click on the above created Package and select New >> Class

3. Put above created methods in that file now.

Wait.java

import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import managers.FileReaderManager;

public class Wait {

public static void untilJqueryIsDone(WebDriver driver){
	untilJqueryIsDone(driver, FileReaderManager.getInstance().getConfigReader().getImplicitlyWait());
}

public static void untilJqueryIsDone(WebDriver driver, Long timeoutInSeconds){
	until(driver, (d) -&gt;
		{
		Boolean isJqueryCallDone = (Boolean)((JavascriptExecutor) driver).executeScript("return jQuery.active==0");
		if (!isJqueryCallDone) System.out.println("JQuery call is in Progress");
		return isJqueryCallDone;
		}, timeoutInSeconds);
}

public static void untilPageLoadComplete(WebDriver driver) {
	untilPageLoadComplete(driver, FileReaderManager.getInstance().getConfigReader().getImplicitlyWait());
}

public static void untilPageLoadComplete(WebDriver driver, Long timeoutInSeconds){
	until(driver, (d) -&gt;
		{
			Boolean isPageLoaded = (Boolean)((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
			if (!isPageLoaded) System.out.println("Document is loading");
			return isPageLoaded;
		}, timeoutInSeconds);
}

public static void until(WebDriver driver, Function&lt;WebDriver, Boolean&gt; waitCondition){
	until(driver, waitCondition, FileReaderManager.getInstance().getConfigReader().getImplicitlyWait());
}


private static void until(WebDriver driver, Function&lt;WebDriver, Boolean&gt; waitCondition, Long timeoutInSeconds){
	WebDriverWait webDriverWait = new WebDriverWait(driver, timeoutInSeconds);
	webDriverWait.withTimeout(timeoutInSeconds, TimeUnit.SECONDS);
	try{
		webDriverWait.until(waitCondition);
	}catch (Exception e){
		System.out.println(e.getMessage());
	}          
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package selenium ;
 
import java . util . concurrent . TimeUnit ;
import java . util . function . Function ;
import org . openqa . selenium . JavascriptExecutor ;
import org . openqa . selenium . WebDriver ;
import org . openqa . selenium . support . ui . WebDriverWait ;
import managers . FileReaderManager ;
 
 
public class Wait {
public static void untilJqueryIsDone ( WebDriver driver ) {
untilJqueryIsDone ( driver , FileReaderManager . getInstance ( ) . getConfigReader ( ) . getImplicitlyWait ( ) ) ;
}
 
public static void untilJqueryIsDone ( WebDriver driver , Long timeoutInSeconds ) {
until ( driver , ( d ) ->
{
Boolean isJqueryCallDone = ( Boolean ) ( ( JavascriptExecutor ) driver ) . executeScript ( “return jQuery.active==0” ) ;
if ( ! isJqueryCallDone ) System . out . println ( “JQuery call is in Progress” ) ;
return isJqueryCallDone ;
} , timeoutInSeconds ) ;
}
public static void untilPageLoadComplete ( WebDriver driver ) {
untilPageLoadComplete ( driver , FileReaderManager . getInstance ( ) . getConfigReader ( ) . getImplicitlyWait ( ) ) ;
}
 
public static void untilPageLoadComplete ( WebDriver driver , Long timeoutInSeconds ) {
until ( driver , ( d ) ->
{
Boolean isPageLoaded = ( Boolean ) ( ( JavascriptExecutor ) driver ) . executeScript ( “return document.readyState” ) . equals ( “complete” ) ;
if ( ! isPageLoaded ) System . out . println ( “Document is loading” ) ;
return isPageLoaded ;
} , timeoutInSeconds ) ;
}
public static void until ( WebDriver driver , Function < WebDriver , Boolean > waitCondition ) {
until ( driver , waitCondition , FileReaderManager . getInstance ( ) . getConfigReader ( ) . getImplicitlyWait ( ) ) ;
}
 
private static void until ( WebDriver driver , Function < WebDriver , Boolean > waitCondition , Long timeoutInSeconds ) {
WebDriverWait webDriverWait = new WebDriverWait ( driver , timeoutInSeconds ) ;
webDriverWait . withTimeout ( timeoutInSeconds , TimeUnit . SECONDS ) ;
try {
webDriverWait . until ( waitCondition ) ;
} catch ( Exception e ) {
System . out . println ( e . getMessage ( ) ) ;
}           
}
}

Note: You will notice that every method name is used twice in the above class. In programming you can have same method names in a same class, if the parameters are different. Now, lets understand why we did that. There can be possibility that an element takes 20 seconds to load whereas other element takes 90 seconds to load. In this case it is good to open your method to accept the time from the method’s user. But, if user is not bothered about the time, then there should not any compulsion to the user to provide the same. So in that case we are setting the timeout by default by our self, which is nothing but the same amount which is specified as Implicit Wait in our property configuration file.

Replace Thread.Sleep in page objects with Wait.untilJqueryIsDone()

We have used lot of Thread.sleep in the code so far, it is the time to replace all with Ajax Wait methods. After changing the code, checkout page object class will look like this:

CheckoutPage.java

import java.util.List;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindAll;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import org.openqa.selenium.support.PageFactory;

import selenium.Wait;
import testDataTypes.Customer;

public class CheckoutPage {
WebDriver driver;

public CheckoutPage(WebDriver driver) {
	this.driver = driver;
    PageFactory.initElements(driver, this);
}

@FindBy(how = How.CSS, using = "#billing_first_name") 
private WebElement txtbx_FirstName;

@FindBy(how = How.CSS, using = "#billing_last_name") 
private WebElement txtbx_LastName;

@FindBy(how = How.CSS, using = "#billing_email") 
private WebElement txtbx_Email;

@FindBy(how = How.CSS, using = "#billing_phone") 
private WebElement txtbx_Phone;

@FindBy(how = How.CSS, using = "#billing_country_field .select2-arrow") 
private WebElement drpdwn_CountryDropDownArrow;

@FindBy(how = How.CSS, using = "#billing_state_field .select2-arrow") 
private WebElement drpdwn_CountyDropDownArrow;

@FindAll(@FindBy(how = How.CSS, using = "#select2-drop ul li"))
private List&lt;WebElement&gt; country_List;	

@FindBy(how = How.CSS, using = "#billing_city") 
private WebElement txtbx_City;

@FindBy(how = How.CSS, using = "#billing_address_1") 
private WebElement txtbx_Address;

@FindBy(how = How.CSS, using = "#billing_postcode") 
private WebElement txtbx_PostCode;

@FindBy(how = How.CSS, using = "#ship-to-different-address-checkbox") 
private WebElement chkbx_ShipToDifferetAddress;

@FindAll(@FindBy(how = How.CSS, using = "ul.wc_payment_methods li"))
private List&lt;WebElement&gt; paymentMethod_List;	

@FindBy(how = How.CSS, using = "#terms.input-checkbox") 
private WebElement chkbx_AcceptTermsAndCondition;

@FindBy(how = How.CSS, using = "#place_order") 
private WebElement btn_PlaceOrder;


public void enter_Name(String name) {
	txtbx_FirstName.sendKeys(name);
}

public void enter_LastName(String lastName) {
	txtbx_LastName.sendKeys(lastName);
}

public void enter_Email(String email) {
	txtbx_Email.sendKeys(email);
}

public void enter_Phone(String phone) {
	txtbx_Phone.sendKeys(phone);
}

public void enter_City(String city) {
	txtbx_City.sendKeys(city);
}

public void enter_Address(String address) {
	txtbx_Address.sendKeys(address);
}

public void enter_PostCode(String postCode) {
	txtbx_PostCode.sendKeys(postCode);
}

public void check_ShipToDifferentAddress(boolean value) {
	if(!value) chkbx_ShipToDifferetAddress.click();
	Wait.untilJqueryIsDone(driver);
}

public void select_Country(String countryName) {
	drpdwn_CountryDropDownArrow.click();
	Wait.untilJqueryIsDone(driver);

	for(WebElement country : country_List){
		if(country.getText().equals(countryName)) {
			country.click();	
			Wait.untilJqueryIsDone(driver);
			break;
		}
	}

}

public void select_County(String countyName) {
	drpdwn_CountyDropDownArrow.click();
	Wait.untilJqueryIsDone(driver);
	for(WebElement county : country_List){
		if(county.getText().equals(countyName)) {
			county.click();	
			//Wait.untilJqueryIsDone(driver);
			break;
		}
	}
}

public void select_PaymentMethod(String paymentMethod) {
	if(paymentMethod.equals("CheckPayment")) {
		paymentMethod_List.get(0).click();
	}else if(paymentMethod.equals("Cash")) {
		paymentMethod_List.get(1).click();
	}else {
		new Exception("Payment Method not recognised : " + paymentMethod);
	}
	Wait.untilJqueryIsDone(driver);
	
}

public void check_TermsAndCondition(boolean value) {
	if(value) chkbx_AcceptTermsAndCondition.click();
}

public void clickOn_PlaceOrder() {
	btn_PlaceOrder.submit();
	Wait.untilPageLoadComplete(driver);
}


public void fill_PersonalDetails(Customer customer) {
	enter_Name(customer.firstName);
	enter_LastName(customer.lastName);
	enter_Phone(customer.phoneNumber.mob);
	enter_Email(customer.emailAddress);
	enter_City(customer.address.city);
	enter_Address(customer.address.streetAddress);
	enter_PostCode(customer.address.postCode);
	select_Country(customer.address.country);
	select_County(customer.address.county);		
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package pageObjects ;
 
import java . util . List ;
import org . openqa . selenium . WebDriver ;
import org . openqa . selenium . WebElement ;
import org . openqa . selenium . support . FindAll ;
import org . openqa . selenium . support . FindBy ;
import org . openqa . selenium . support . How ;
import org . openqa . selenium . support . PageFactory ;
 
import selenium . Wait ;
import testDataTypes . Customer ;
 
public class CheckoutPage {
WebDriver driver ;
public CheckoutPage ( WebDriver driver ) {
this . driver = driver ;
     PageFactory . initElements ( driver , this ) ;
}
@FindBy ( how = How . CSS , using = “#billing_first_name” )
private WebElement txtbx_FirstName ;
@FindBy ( how = How . CSS , using = “#billing_last_name” )
private WebElement txtbx_LastName ;
@FindBy ( how = How . CSS , using = “#billing_email” )
private WebElement txtbx_Email ;
@FindBy ( how = How . CSS , using = “#billing_phone” )
private WebElement txtbx_Phone ;
@FindBy ( how = How . CSS , using = “#billing_country_field .select2-arrow” )
private WebElement drpdwn_CountryDropDownArrow ;
@FindBy ( how = How . CSS , using = “#billing_state_field .select2-arrow” )
private WebElement drpdwn_CountyDropDownArrow ;
@FindAll ( @FindBy ( how = How . CSS , using = “#select2-drop ul li” ) )
private List <WebElement> country_List ;
@FindBy ( how = How . CSS , using = “#billing_city” )
private WebElement txtbx_City ;
@FindBy ( how = How . CSS , using = “#billing_address_1” )
private WebElement txtbx_Address ;
@FindBy ( how = How . CSS , using = “#billing_postcode” )
private WebElement txtbx_PostCode ;
@FindBy ( how = How . CSS , using = “#ship-to-different-address-checkbox” )
private WebElement chkbx_ShipToDifferetAddress ;
@FindAll ( @FindBy ( how = How . CSS , using = “ul.wc_payment_methods li” ) )
private List <WebElement> paymentMethod_List ;
@FindBy ( how = How . CSS , using = “#terms.input-checkbox” )
private WebElement chkbx_AcceptTermsAndCondition ;
@FindBy ( how = How . CSS , using = “#place_order” )
private WebElement btn_PlaceOrder ;
public void enter_Name ( String name ) {
txtbx_FirstName . sendKeys ( name ) ;
}
public void enter_LastName ( String lastName ) {
txtbx_LastName . sendKeys ( lastName ) ;
}
 
public void enter_Email ( String email ) {
txtbx_Email . sendKeys ( email ) ;
}
public void enter_Phone ( String phone ) {
txtbx_Phone . sendKeys ( phone ) ;
}
public void enter_City ( String city ) {
txtbx_City . sendKeys ( city ) ;
}
public void enter_Address ( String address ) {
txtbx_Address . sendKeys ( address ) ;
}
public void enter_PostCode ( String postCode ) {
txtbx_PostCode . sendKeys ( postCode ) ;
}
public void check_ShipToDifferentAddress ( boolean value ) {
if ( ! value ) chkbx_ShipToDifferetAddress . click ( ) ;
Wait . untilJqueryIsDone ( driver ) ;
}
public void select_Country ( String countryName ) {
drpdwn_CountryDropDownArrow . click ( ) ;
Wait . untilJqueryIsDone ( driver ) ;
 
for ( WebElement country : country_List ) {
if ( country . getText ( ) . equals ( countryName ) ) {
country . click ( ) ;
Wait . untilJqueryIsDone ( driver ) ;
break ;
}
}
 
}
public void select_County ( String countyName ) {
drpdwn_CountyDropDownArrow . click ( ) ;
Wait . untilJqueryIsDone ( driver ) ;
for ( WebElement county : country_List ) {
if ( county . getText ( ) . equals ( countyName ) ) {
county . click ( ) ;
break ;
}
}
}
public void select_PaymentMethod ( String paymentMethod ) {
if ( paymentMethod . equals ( “CheckPayment” ) ) {
paymentMethod_List . get ( 0 ) . click ( ) ;
} else if ( paymentMethod . equals ( “Cash” ) ) {
paymentMethod_List . get ( 1 ) . click ( ) ;
} else {
new Exception ( "Payment Method not recognised : " + paymentMethod ) ;
}
Wait . untilJqueryIsDone ( driver ) ;
}
public void check_TermsAndCondition ( boolean value ) {
if ( value ) chkbx_AcceptTermsAndCondition . click ( ) ;
}
public void clickOn_PlaceOrder ( ) {
btn_PlaceOrder . submit ( ) ;
Wait . untilPageLoadComplete ( driver ) ;
}
public void fill_PersonalDetails ( Customer customer ) {
enter_Name ( customer . firstName ) ;
enter_LastName ( customer . lastName ) ;
enter_Phone ( customer . phoneNumber . mob ) ;
enter_Email ( customer . emailAddress ) ;
enter_City ( customer . address . city ) ;
enter_Address ( customer . address . streetAddress ) ;
enter_PostCode ( customer . address . postCode ) ;
select_Country ( customer . address . country ) ;
select_County ( customer . address . county ) ;
}
 
}

Run the Cucumber Test

Run as JUnit

Now we are all set to run the Cucumber test. Right Click on TestRunner class and Click Run As  >> JUnit TestCucumber will run the script the same way it runs in Selenium WebDriver and the result will be shown in the left hand side project explorer window in JUnit tab.

You will notice that after executing all the steps, the execution will come in the hooks and it will execute quitDriver().

Project Explorer

阿贾克斯等待5Ajax Wait 5

For more updates on Cucumber Tutorial, please Subscribe to our Newsletter.

Please ask questions on ForumsQA, in case of any issues or doubts.

猜你喜欢

转载自blog.csdn.net/kuangjuelian229/article/details/88617326