最常見的測試自動化挑戰(zhàn)之一是我們?nèi)绾涡薷?Selenium WebDriver
? 中的請求標頭。作為一名自動化測試人員,你會遇到任何編程語言(包括 Java)的這一挑戰(zhàn)。在提出解決方案之前,我們需要更好地理解問題陳述,并在使用 ?Selenium WebDriver
? 的同時,在 Java 中修改頭部請求的不同可能性。在接下來的文章里,我們將學習如何使用 Selenium WebDriver 和不同的可用選項在 Java 中修改 HTTP 請求標頭。
什么是 HTTP 標頭
HTTP 標頭是 HTTP 協(xié)議的重要組成部分。它們定義了 HTTP 消息(請求或響應(yīng))并允許客戶端和服務(wù)器與消息交換可選的元數(shù)據(jù)。它們由不區(qū)分大小寫的頭字段名稱后跟一個冒號,然后是頭字段值組成。標題字段可以擴展到多行,方法是在每一額外行前至少有一個空格或水平制表符。
標題可以根據(jù)其上下文進行分組:
- 請求標頭:HTTP 請求標頭用于提供有關(guān)正在獲取的資源和發(fā)出請求的客戶端的附加信息。
- 響應(yīng)頭:HTTP 響應(yīng)頭提供有關(guān)響應(yīng)的信息。Location 標頭指定資源的位置,服務(wù)器標頭提供有關(guān)提供資源的服務(wù)器的信息。
- 表示頭:HTTP 表示頭是任何 HTTP 響應(yīng)的重要組成部分。它們提供有關(guān)協(xié)議元素的信息,如 MIME 類型、字符編碼等。這使它們成為通過 Internet 處理資源的重要組成部分。
- 有效載荷標頭:HTTP 有效載荷標頭包含有關(guān) HTTP 消息有效載荷的數(shù)據(jù)(例如其長度和編碼),但與表示無關(guān)。
深入研究 HTTP 請求標頭
HTTP 請求標頭是一種通信機制,使瀏覽器或客戶端能夠從(Web)服務(wù)器請求特定網(wǎng)頁或數(shù)據(jù)。在 Web 通信或 Internet 瀏覽中使用時,HTTP 請求標頭使瀏覽器和客戶端能夠通過發(fā)送請求與適當?shù)?Web 服務(wù)器進行通信。
HTTP 請求標頭描述了 Web 瀏覽器發(fā)送的加載頁面的請求。它也被稱為客戶端到服務(wù)器協(xié)議。標頭包括客戶端請求的詳細信息,例如用戶使用的瀏覽器類型和操作系統(tǒng)以及在屏幕上正確顯示請求內(nèi)容所需的其他參數(shù)。
以下是 HTTP 請求標頭中包含的主要信息:
- IP 地址(來源)和端口號。
- 請求的網(wǎng)頁的 URL。
- Web 服務(wù)器或目標網(wǎng)站(主機)。
- 瀏覽器將接受的數(shù)據(jù)類型(文本、html、xml 等)。
- 發(fā)送兼容數(shù)據(jù)的瀏覽器類型(Mozilla、Chrome、IE)。
作為響應(yīng),包含請求數(shù)據(jù)的 HTTP 響應(yīng)標頭由 發(fā)回。
需要更改 HTTP 請求標頭
你能猜到為什么我們甚至需要在已經(jīng)設(shè)置到腳本中后更改請求頭嗎?
以下是你可能需要更改 HTTP 請求標頭的一些場景:
- 通過建立適當?shù)?HTTP 標頭來測試控制和/或測試不同的變體。
- 需要對 Web 應(yīng)用程序的不同方面甚至服務(wù)器邏輯進行徹底測試的情況。
- 由于 HTTP 請求標頭用于啟用 Web 應(yīng)用程序邏輯的某些特定部分,通常在正常模式下會禁用這些部分,因此根據(jù)測試場景,可能需要不時修改 HTTP 請求標頭。
在被測 Web 應(yīng)用程序上測試訪客模式是你可能需要修改 HTTP 請求標頭的理想情況。
但是?Selenium RC
?曾經(jīng)支持的修改HTTP請求頭的功能,現(xiàn)在?Selenium Webdriver
?不處理了。
這就是為什么在使用?Selenium
?框架和 ?Java
?編寫測試自動化項目時,我們?nèi)绾胃??header
?請求的問題。
如何在 Selenium Java 項目中修改頭請求
在 Selenium Java 教程的這一部分中,我們將了解在 Java 中修改標頭請求的多種方法。大體上,有幾種可能,接下來可以修改 ?Java-Selenium
? 項目中的頭請求。
- 使用像 ?
REST Assured
? 這樣的驅(qū)動程序/庫而不是 ?Selenium
?。 - 使用反向代理,例如瀏覽器 ?
mob-proxy
? 或其他一些代理機制。 - 使用 ?
Firefox
?瀏覽器擴展,這將有助于修改請求的標頭。
讓我們一一探討每一種可能性:
使用 REST Assured 庫修改 HTTP 請求標頭
與 Selenium 一起,我們可以使用 REST Assured,它是一種以簡單方式使用 REST 服務(wù)的絕佳工具。
在任何 IDE(例如 Eclipse)中為你的項目配置 REST Assured 的先決條件非常簡單。設(shè)置 ?Java
?、?Eclipse
?和?TestNG
? 后,你需要下載所需的REST Assured jar 文件。
jar 文件下載后,你必須在 Eclipse 中創(chuàng)建一個項目,并將下載的 jar 文件作為外部 jar 添加到 ?Properties
?部分。這再次類似于我們將 ?Selenium jar
? 文件添加到項目的方式。使用 REST Assured 庫成功設(shè)置 Java 項目后,就可以開始了。
我們打算創(chuàng)建一種機制,以便可以自定義請求標頭。為了以上述可能性實現(xiàn)這一點,我們首先需要了解創(chuàng)建請求標頭的常規(guī)方法。
讓我們考慮以下場景:
- 我們有一個名為 ?
RequestHeaderChangeDemo
?的 Java 類,我們在其中維護基本配置 - 我們有一個名為 ?
TestSteps
?的測試步驟文件,我們將在其中調(diào)用 ?RequestHeaderChangeDemo
?Java 類中的方法,通過這些方法我們將執(zhí)行我們的測試。
觀察下面名為 ?RequestHeaderChangeDemo
?的 Java 類。
BASE_URL 是應(yīng)用了以下四種方法的亞馬遜網(wǎng)站:
- 認證用戶
- 獲取產(chǎn)品
- 添加產(chǎn)品
- 移除產(chǎn)品
public class RequestHeaderChangeDemo
{
private static final String BASE_URL = "https://amazon.com";
public static IRestResponse<Token> authenticateUser(AuthorizationRequest authRequest) {
RestAssured.baseURI = BASE_URL;
RequestSpecification request = RestAssured.given();
request.header("Content-Type", "application/json");
Response response = request.body(authRequest).post(Route.generateToken());
return new RestResponse(Token.class, response);
}
public static IRestResponse<Products> getProducts()
{
RestAssured.baseURI = BASE_URL;
RequestSpecification request = RestAssured.given();
request.header("Content-Type", "application/json");
Response response = request.get(Route.products());
return new RestResponse(Products.class, response);
}
public static IRestResponse<UserAccount> addProduct(AddProductsRequest addProductsRequest, String token)
{
RestAssured.baseURI = BASE_URL;
RequestSpecification request = RestAssured.given();
request.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json");
Response response = request.body(addProductsRequest).post(Route.products());
return new RestResponse(UserAccount.class, response);
}
public static Response removeProduct(RemoveProductRequest removeProductRequest, String token)
{
RestAssured.baseURI = BASE_URL;
RequestSpecification request = RestAssured.given();
request.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json");
return request.body(removeProductRequest).delete(Route.product());,
}
}
在上面的Java類文件中,我們在每個連續(xù)的方法中重復(fù)發(fā)送了?BASE_URL
?和?headers
?。示例如下所示:
RestAssured.baseURI = BASE_URL;
RequestSpecification request = RestAssured.given();
request.header("Content-Type", "application/json");
Response response = request.body(authRequest).post(Route.generateToken());
?request.header
? 方法請求 JSON 格式的標頭。有大量的代碼重復(fù),這降低了代碼的可維護性。
如果我們在構(gòu)造函數(shù)中初始化 ?RequestSpecification
?對象并使這些方法非靜態(tài)(即創(chuàng)建實例方法),則可以避免這種情況。
由于 Java 中的實例方法屬于類的 Object 而不是類本身,因此即使在創(chuàng)建類的 Object 之后也可以調(diào)用該方法。與此同時,我們還將覆蓋實例方法。
將方法轉(zhuǎn)換為實例方法有以下優(yōu)點:
- 身份驗證僅在一個 ?
RequestSpecification
?對象中進行一次。不再需要為其他請求創(chuàng)建相同的請求。 - 靈活修改項目中的請求頭。
因此,讓我們看看當我們使用實例方法時 Java 類 ?RequestHeaderChangeDemo
?和測試步驟文件 ?TestSteps
? 的外觀。
帶有實例方法的 ?RequestHeaderChangeDemo
?類的 Java 類:
public class RequestHeaderChangeDemo
{
private final RequestSpecification request;
public RequestHeaderChangeDemo(String baseUrl)
{
RestAssured.baseURI = baseUrl;
request = RestAssured.given();
request.header("Content-Type", "application/json");
}
public void authenticateUser(AuthorizationRequest authRequest)
{
Response response = request.body(authRequest).post(Route.generateToken());
if (response.statusCode() != HttpStatus.SC_OK)
throw new RuntimeException("Authentication Failed. Content of failed Response: " + response.toString() + " , Status Code : " + response.statusCode());
Token tokenResponse = response.body().jsonPath().getObject("$", Token.class);
request.header("Authorization", "Bearer " + tokenResponse.token);
}
public IRestResponse<Products> getProducts()
{
Response response = request.get(Route.products());
return new RestResponse(Products.class, response);
}
public IRestResponse<UserAccount> addProduct(AddProductsRequest addProductsRequest)
{
Response response = request.body(addProductsRequest).post(Route.products());
return new RestResponse(UserAccount.class, response);
}
public Response removeProducts(RemoveProductRequest removeProductRequest)
{
return request.body(removeProductRequest).delete(Route.product());
}
}
我們創(chuàng)建了一個構(gòu)造函數(shù)來初始化包含 ?BaseURL
?和請求標頭的 ?RequestSpecification
?對象。代碼演練
- 早些時候,我們必須在每個請求標頭中傳遞令牌。現(xiàn)在,一旦我們在方法?
authenticateUser()
? 中收到令牌響應(yīng),我們就將它放入請求的同一個實例中。這使測試步驟的執(zhí)行能夠向前推進,而無需像之前那樣為每個請求添加令牌。這使得標頭可用于對服務(wù)器的后續(xù)調(diào)用。 - 現(xiàn)在將在 ?
TestSteps
?文件中初始化這個 ?RequestHeaderChangeDemo
?Java 類。
我們根據(jù) ?RequestHeaderChangeDemo
?Java 類中的更改而更改 ?TestSteps
?文件。
public class TestSteps
{
private final String USER_ID = " (Enter the user id from your test case )";
private Response response;
private IRestResponse<UserAccount> userAccountResponse;
private Product product;
private final String BaseUrl = "https://amazon.com";
private RequestHeaderChangeDemo endPoints;
@Given("^User is authorized$")
public void authorizedUser()
{
endPoints = new RequestHeaderChangeDemo (BaseUrl);
AuthorizationRequest authRequest = new AuthorizationRequest("(Username)", "(Password)");
endPoints.authenticateUser(authRequest);
}
@Given("^Available Product List$")
public void availableProductLists()
{
IRestResponse<Products> productsResponse = endPoints.getProducts();
Product = productsResponse.getBody().products.get(0);
}
@When("^Adding the Product in Wishlist$")
public void addProductInWishList()
{
ADDPROD code = new ADDPROD(product.code);
AddProductsRequest addProductsRequest = new AddProductsRequest(USER_ID, code);
userAccountResponse = endPoints.addProduct(addProductsRequest);
}
@Then("^The productis added$")
public void productIsAdded()
{
Assert.assertTrue(userAccountResponse.isSuccessful());
Assert.assertEquals(201, userAccountResponse.getStatusCode());
Assert.assertEquals(USER_ID, userAccountResponse.getBody().userID);
Asert.assertEquals(product.code, userAccountResponse.getBody().products.get(0).code);
}
@When("^Product to be removed from the list$")
public void removeProductFromList()
{
RemoveProductRequest removeProductRequest = new RemoveProductRequest(USER_ID, product.code);
response = endPoints.removeProduct(removeProductRequest);
}
@Then("^Product is removed$")
public void productIsRemoved()
{
Assert.assertEquals(204, response.getStatusCode());
userAccountResponse = endPoints.getUserAccount(USER_ID);
Assert.assertEquals(200, userAccountResponse.getStatusCode());
Assert.assertEquals(0, userAccountResponse.getBody().products.size());
}
}
這是我們在修改后的實現(xiàn)中所做的:代碼演練
- 初始化 ?
RequestHeaderChangeDemo
?類對象作為端點。 - ?
BaseURL
?是在第一個方法(即?authorizedUser
?)中傳遞的。 - 在方法?
authorizedUser
?中,我們調(diào)用了?RequestHeaderChangeDemo
?類的構(gòu)造函數(shù)?authenticateUser
?。 - 因此,后續(xù)步驟定義使用相同的端點對象。
使用瀏覽器 Mob-Proxy 等反向代理修改 HTTP 請求標頭
顧名思義,在 Java-Selenium 自動化測試套件中處理請求標頭更改時,我們可以選擇使用代理。由于 Selenium 禁止在瀏覽器和服務(wù)器中注入信息,因此可以使用代理進行救援。
如果測試是在公司防火墻后面執(zhí)行的,則這種方法不是首選。
作為 Web 基礎(chǔ)架構(gòu)組件,代理通過將自身定位在客戶端和服務(wù)器之間來使 Web 流量通過它。在企業(yè)界,代理的工作方式類似,使流量通過它,允許安全的流量通過并阻止?jié)撛谕{。代理具有部分或完全修改請求和響應(yīng)的能力。
核心思想是發(fā)送授權(quán)標頭,繞過包含憑證對話的階段,也稱為基本認證對話。然而,結(jié)果證明這是一個累人的過程,尤其是在測試用例需要頻繁重新配置的情況下。
這就是瀏覽器 mob-proxy 庫的用武之地。當你將代理配置作為Selenium 自動化測試套件的一部分時,代理配置將在你每次執(zhí)行測試套件時有效。
讓我們看看如何將瀏覽器 mob-proxy 與使用基本身份驗證保護的示例網(wǎng)站一起使用。為了解決這個問題,我們可能會縮小兩種可能的方法:
- 向所有請求添加授權(quán)標頭,沒有條件或例外。
- 僅向滿足特定條件的請求添加標頭。
盡管我們不會解決標頭管理問題,但我們?nèi)詫⒀菔救绾卧跒g覽器 mob-proxy 授權(quán)工具集的幫助下解決授權(quán)問題。
在 Selenium Java 教程的這一部分中,我們將只關(guān)注第一種方法(即向所有請求添加授權(quán)標頭)。
首先我們在?pom.xml
?中添加?browsermob-proxy
?的依賴
如果要將此方法傳遞給所有標頭請求,即特定代理,在這種情況下,應(yīng)調(diào)用 ?forAllProxy
?方法,如下所示:
public void forAllProxy()
{
proxy = new BrowserMobProxyServer();
try {
String authHeader = "Basic " + Base64.getEncoder().encodeToString("webelement:click".getBytes("utf-8"));
proxy.addHeader("checkauth", authfirstHeader);
}
catch (UnsupportedEncodingException e)
{
System.err.println("the Authorization can not be passed");
e.printStackTrace();
}
proxy.start(0);
}
public class caseFirstTest
{
WebDriver driver;
BrowserMobProxy proxy;
@BeforeAll
public static void globalSetup()
{
System.setProperty("webdriver.gecko.driver", "(path of the driver)");
}
@BeforeEach
public void setUp()
{
setUpProxy();
FirefoxOptions Options = new FirefoxOptions();
Options.setProxy(ClientUtil.createSeleniumProxy(proxy));
driver = new FirefoxDriver(Options);
}
@Test
public void testBasicAuth()
{
driver.get("https://webelement.click/stand/basic?lang=en");
Wait<webdriver> waiter = new FluentWait(driver).withTimeout(Duration.ofSeconds(50)).ignoring(NoSuchElementException.class);
String greetings = waiter.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("(Mention the xpath)"))).getText();
Assertions.assertEquals("(message");
}
@AfterEach
public void tearDown()
{
if(driver != null)
{
driver.quit();
}
if(proxy != null)
{
proxy.stop();
}
}
private void setUpProxy(
{
}
}
</webdriver>
在上面的代碼中,以 ?String authHeader
? 開頭的行表示我們正在創(chuàng)建標頭,這將被添加到請求中。之后,這些請求會通過我們在 ?proxy.addHeader(“checkauth”, authfirstHeader)
? 中創(chuàng)建的代理傳遞。
try {
String authHeader = "Basic " + Base64.getEncoder().encodeToString("webelement:click".getBytes("utf-8"));
proxy.addHeader("checkauth", authfirstHeader);
}
catch (UnsupportedEncodingException e)
{
………………………………………………………………………………
………………………………………………………………………………
……………………………………………………………………………...
}
proxy.start(0);
}
最后,我們啟動代理設(shè)置?0
?來標記?start
?參數(shù),代理在端口上啟動。
使用 Firefox 擴展修改 HTTP 請求頭
在 Selenium Java 教程的這一部分中,我們將了解如何使用適當?shù)?Firefox 瀏覽器擴展來修改標頭請求。此選項的主要缺點是它僅適用于 Firefox(而不適用于 Chrome、Edge 等其他瀏覽器)。
執(zhí)行以下步驟以使用 Firefox 擴展修改 HTTP 請求標頭:
- 下載 Firefox 瀏覽器擴展
- 加載擴展。
- 設(shè)置擴展首選項。
- 設(shè)置所需的功能。
- 準備測試自動化腳本。
讓我們一步一步來:
1. 下載火狐瀏覽器擴展
用 ?.*xpi
? 搜索 firefox 擴展名并在項目中設(shè)置
2.加載火狐擴展
參考以下代碼添加 Firefox 配置文件:
FirefoxProfile profile = new FirefoxProfile();
File modifyHeaders = new File(System.getProperty("user.dir") + "/resources/modify_headers.xpi");
profile.setEnableNativeEvents(false);
try {
profile.addExtension(modifyHeaders);
}
catch (IOException e)
{
e.printStackTrace();
}
一旦我們將 Firefox 擴展加載到項目中,我們設(shè)置首選項(即在觸發(fā)擴展之前需要設(shè)置的各種輸入)。這是使用 ?profile.setPreference
? 方法完成的。
3.設(shè)置擴展首選項
此方法通過鍵集參數(shù)機制設(shè)置任何給定配置文件的首選項。這里的第一個參數(shù)是設(shè)置值的鍵,第二個參數(shù)設(shè)置相應(yīng)的整數(shù)值。
這是參考實現(xiàn):
profile.setPreference("modifyheaders.headers.count", 1);
profile.setPreference("modifyheaders.headers.action0", "Add");
profile.setPreference("modifyheaders.headers.name0", "Value");
profile.setPreference("modifyheaders.headers.value0", "numeric value");
profile.setPreference("modifyheaders.headers.enabled0", true);
profile.setPreference("modifyheaders.config.active", true);
profile.setPreference("modifyheaders.config.alwaysOn", true);
在上面的代碼中,我們列出了我們想要設(shè)置 ?header
?實例的次數(shù)。
profile.setPreference("modifyheaders.headers.count", 1)
接下來,我們指定操作,標頭名稱和標頭值包含從 ?API
?調(diào)用動態(tài)接收的值。
profile.setPreference("modifyheaders.headers.action0", "Add");
對于? .setPreference
? 實現(xiàn)的其余部分,我們啟用 ?all
?以便它允許在 ?WebDriver
?實例化 Firefox 瀏覽器時加載擴展,并使用 HTTP 標頭將擴展設(shè)置為活動模式。
4. 設(shè)置所需的功能
Selenium 中的 ?Desired Capabilities
? 用于設(shè)置需要執(zhí)行自動化測試的瀏覽器、瀏覽器版本和平臺類型。
在這里,我們?nèi)绾卧O(shè)置所需的功能:
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setBrowserName("firefox");
capabilities.setPlatform(org.openqa.selenium.Platform.ANY);
capabilities.setCapability(FirefoxDriver.PROFILE, profile);
WebDriver driver = new FirefoxDriver(capabilities);
driver.get("url");
如果你想使用未安裝在本地(或測試)計算機上的 Firefox 版本修改 HTTP 請求標頭,該怎么辦。這就是 ?LambdaTest
?,最大的基于云的自動化測試平臺,提供更快的跨瀏覽器測試基礎(chǔ)設(shè)施來拯救。
使用 ?LambdaTest
?,你可以靈活地修改不同瀏覽器和平臺組合的 HTTP 請求標頭。如果你愿意使用 Firefox 擴展修改 HTTP 請求標頭, 你可以使用 ?LambdaTest
?在不同版本的 Firefox 瀏覽器上實現(xiàn)相同的功能。
5. 起草整個測試自動化腳本
完成上述所有步驟后,我們將繼續(xù)設(shè)計整個測試自動化腳本:
public void startwebsite()
{
FirefoxProfile profile = new FirefoxProfile();
File modifyHeaders = new File(System.getProperty("user.dir") + "/resources/modify_headers.xpi");
profile.setEnableNativeEvents(false);
try
{
profile.addExtension(modifyHeaders);
}
catch (IOException e)
{
e.printStackTrace();
}
profile.setPreference("modifyheaders.headers.count", 1);
profile.setPreference("modifyheaders.headers.action0", "Add");
profile.setPreference("modifyheaders.headers.name0", "Value");
profile.setPreference("modifyheaders.headers.value0", "Numeric Value");
profile.setPreference("modifyheaders.headers.enabled0", true);
profile.setPreference("modifyheaders.config.active", true);
profile.setPreference("modifyheaders.config.alwaysOn", true);
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setBrowserName("firefox");
capabilities.setPlatform(org.openqa.selenium.Platform.ANY);
capabilities.setCapability(FirefoxDriver.PROFILE, profile);
WebDriver driver = new FirefoxDriver(capabilities);
driver.get("url");
}
結(jié)論
在這個 Selenium Java 教程中,我們探索了三種不同的方法來處理對 HTTP 請求標頭的修改。Selenium 本身就是一個很棒的工具,并且在 Web 自動化測試中一直運行良好。