測試自動化挑戰之一修改Selenium WebDriver中的請求請求頭。我將分享如何使用Selenium WebDriver修改HTTP請求請求頭。
什么是HTTP請求頭
HTTP請求頭是HTTP 協議的重要組成部分。它們定義了 HTTP 消息(請求或響應)並允許客戶端和服務器與消息交換可選的元數據。它們由不區分大小寫的頭字段名稱后跟一個冒號,然后是頭字段值組成。標題字段可以擴展到多行,方法是在每一額外行前至少有一個空格或水平制表符。
標題可以根據其上下文進行分組:
- 請求頭:HTTP 請求請求頭用於提供有關正在獲取的資源和發出請求的客戶端的附加信息。
- 響應頭:HTTP 響應頭提供有關響應的信息。
以下是 HTTP 請求請求頭中包含的主要信息:
- IP 地址(來源)和端口號。
- 請求的網頁的 URL。
- Web 服務器或目標網站(主機)。
- 瀏覽器將接受的數據類型(文本、html、xml 等)。
- 發送兼容數據的瀏覽器類型(Mozilla、Chrome、IE)。
- 作為響應,包含請求數據的 HTTP 響應請求頭由 發回。
需要更改HTTP請求請求頭
以下是測試工作中可能需要更改 HTTP 請求請求頭的一些場景:
- 通過建立適當的 HTTP 請求頭來測試控制、測試不同的版本。
- 需要對 Web 應用程序的不同方面甚至服務器邏輯進行徹底測試的情況。
- 由於 HTTP 請求請求頭用於啟用 Web 應用程序邏輯的某些特定部分,通常在正常模式下會禁用這些部分,因此根據測試場景,可能需要不時修改 HTTP 請求請求頭。
在被測 Web 應用程序上測試訪客模式是測試人員可能需要修改HTTP請求請求頭的情況。但是Selenium RC曾經支持的修改HTTP請求頭的功能,現在Selenium Webdriver不處理了。
Selenium修改請求頭
Selenium Java中修改請求頭請求的多種方法。大體上,有幾種可能,接下來可以修改 Java-Selenium 項目中的頭請求。
- 使用Java HTTP請求框架。
- 使用反向代理。
- 使用 Firefox 瀏覽器擴展。
Java HTTP請求框架
與 Selenium 一起,我們可以使用 REST Assured,它是一種以簡單方式使用 REST服務的絕佳工具。為項目配置 REST Assured教程非常簡單,這里就不介紹了。
讓我們考慮以下場景:
- 我們有一個名為 RequestHeaderChangeDemo 的 Java 類,我們在其中維護基本配置
- 我們有一個名為 TestSteps 的測試步驟文件,我們將在其中調用 RequestHeaderChangeDemo Java 類中的方法,通過這些方法我們將執行我們的測試。
觀察下面名為 RequestHeaderChangeDemo 的 Java 類。
BASE_URL是應用了以下四種方法的網站:
- 認證用戶
- 獲取產品
- 添加產品
- 移除產品
public class RequestHeaderChangeDemo {
private static final String BASE_URL = "https://****";
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);
}
此處省略部分重復代碼
}
在上面的Java類文件中,我們在每個連續的方法中重復發送了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 格式的請求頭。有大量的代碼重復,這降低了代碼的可維護性。如果我們在構造函數中初始化RequestSpecification對象並使這些方法非靜態(即創建實例方法),則可以避免這種情況。由於 Java 中的實例方法屬於類的 Object 而不是類本身,因此即使在創建類的 Object之后也可以調用該方法。與此同時,我們還將重寫實例方法。
將方法轉換為實例方法有以下優點:
- 身份驗證僅在一個 RequestSpecification 對象中進行一次。不再需要為其他請求創建相同的請求。
- 靈活修改項目中的請求頭。
因此,讓我們看看當我們使用實例方法時 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);
}
此處省略部分代碼
}
代碼實戰
- 我們創建了一個構造函數來初始化包含 BaseURL 和請求請求頭的 RequestSpecification 對象。
- 早些時候,我們必須在每個請求請求頭中傳遞令牌。現在,一旦我們在方法authenticateUser() 中收到令牌響應,我們就將它放入請求的同一個實例中。這使測試步驟的執行能夠向前推進,而無需像之前那樣為每個請求添加令牌。這使得請求頭可用於對服務器的后續調用。
- 現在將在 TestSteps 文件中初始化這個 RequestHeaderChangeDemo Java 類。
我們根據 RequestHeaderChangeDemo Java 類中的更改更改 TestSteps 文件。
public class TestSteps
{
private final String USER_ID = "";
private Response response;
private IRestResponse<UserAccount> userAccountResponse;
private Product product;
private final String BaseUrl = "https://******";
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$")
{
ADDPROD code = new ADDPROD(product.code);
AddProductsRequest addProductsRequest = new AddProductsRequest(USER_ID, code);
userAccountResponse = endPoints.addProduct(addProductsRequest);
}
}
這是我們在修改后的實現中所做的:
- 初始化 RequestHeaderChangeDemo 類對象作為端點。
- BaseURL 是在第一個方法(即authorizedUser)中傳遞的。
- 在方法authorizedUser 中,我們調用了RequestHeaderChangeDemo 類的構造函數authenticateUser。
- 因此,后續步驟定義使用相同的端點對象。
使用反向代理
顧名思義,在 Java-Selenium 自動化測試套件中處理請求請求頭更改時,我們可以選擇使用代理。由於 Selenium 禁止在瀏覽器和服務器中注入信息,因此可以使用代理進行處理。如果測試是在公司防火牆后面執行的,則這種方法不是首選。
作為 Web 基礎架構組件,代理通過將自身定位在客戶端和服務器之間來使 Web 流量通過它。代理的工作方式類似,使流量通過它,允許安全的流量通過並阻止潛在威脅。代理具有部分或完全修改請求和響應的能力。
核心思想是發送授權請求頭,繞過包含憑證對話的階段,也稱為基本認證對話。然而,結果證明這是一個累人的過程,尤其是在測試用例需要頻繁重新配置的情況下。
這就是瀏覽器 mob-proxy 庫的用武之地。讓我們看看如何將瀏覽器 mob-proxy 與使用基本身份驗證保護的示例網站一起使用。為了解決這個問題,我們可能會縮小兩種可能的方法:
- 向所有請求添加授權請求頭,沒有條件或例外。
- 僅向滿足特定條件的請求添加請求頭。
盡管我們不會解決請求頭管理問題,但我們仍將演示如何在瀏覽器 mob-proxy 授權工具集的幫助下解決授權問題。在 Selenium Java 教程的這一部分中,我們將只展示了第一種方法(即向所有請求添加授權請求頭)。
首先我們在pom.xml中添加browsermob-proxy的依賴
<dependencies>
<dependency>
<groupId>net.lightbody.bmp</groupId>
<artifactId>browsermob-core</artifactId>
<version>2.1.5</version>
<scope>test</scope>
</dependency>
</dependencies>
然后需要在代碼做一些改造:
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(
{
}
}
如果要將此方法傳遞給所有請求頭請求,即特定代理,在這種情況下,應調用 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);
}
在上面的代碼中,以 authHeader 開頭的行表示我們正在創建請求頭,這將被添加到請求中。之后,這些請求會通過我們在 proxy.addHeader(“checkauth”, authfirstHeader) 中創建的代理傳遞。
try {
String authHeader = "Basic " + Base64.getEncoder().encodeToString("webelement:click".getBytes("utf-8"));
proxy.addHeader("checkauth", authfirstHeader);
}
catch (UnsupportedEncodingException e)
{
……………………
}
proxy.start(0);
}
最后,我們啟動代理設置0來標記start參數,代理在端口上啟動。
使用 Firefox 擴展
下面分享如何使用適當的 Firefox 瀏覽器擴展來修改請求頭請求。此選項的主要缺點是它僅適用於 Firefox(而不適用於 Chrome、Edge 等其他瀏覽器),現在很少用Firefox做測試了,簡單學習一下。
執行以下步驟以使用 Firefox 擴展修改 HTTP 請求請求頭:
- 下載 Firefox 瀏覽器擴展
- 加載擴展。
- 設置擴展首選項。
- 設置所需的功能。
- 准備測試自動化腳本。
讓我們一步一步來:
下載火狐瀏覽器擴展
自行解決。
加載火狐擴展
參考以下代碼添加 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 擴展加載到項目中,我們設置首選項(即在觸發擴展之前需要設置的各種輸入)。這是使用 profile.setPreference 方法完成的。
此方法通過鍵集參數機制設置任何給定配置文件的首選項。這里的第一個參數是設置值的鍵,第二個參數設置相應的整數值。
這是參考實現:
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);
在上面的代碼中,我們列出了我們想要設置 header 實例的次數。
profile.setPreference("modifyheaders.headers.count", 1);
接下來,我們指定操作,請求頭名稱和請求頭值包含從 API 調用動態接收的值。
profile.setPreference("modifyheaders.headers.action0", "Add");
對於實現的其余部分,我們啟用 all 以便它允許在 WebDriver 實例化 Firefox 瀏覽器時加載擴展,並使用 HTTP 請求頭將擴展設置為活動模式。
設置所需的功能
Selenium 中的 Desired Capabilities 用於設置需要執行自動化測試的瀏覽器、瀏覽器版本和平台類型。
在這里,我們如何設置所需的功能:
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");
完整自動化用例
完成上述所有步驟后,我們將繼續設計整個測試自動化腳本:
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");
}