App下載

如何使用 Selenium WebDriver 修改 JAVA 中的 HTTP 請求頭?

盛大富翁 2021-09-10 10:19:59 瀏覽數(shù) (5623)
反饋

最常見的測試自動化挑戰(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ù)其上下文進行分組:

  1. 請求標頭:HTTP 請求標頭用于提供有關(guān)正在獲取的資源和發(fā)出請求的客戶端的附加信息。
  2. 響應(yīng)頭:HTTP 響應(yīng)頭提供有關(guān)響應(yīng)的信息。Location 標頭指定資源的位置,服務(wù)器標頭提供有關(guān)提供資源的服務(wù)器的信息。
  3. 表示頭:HTTP 表示頭是任何 HTTP 響應(yīng)的重要組成部分。它們提供有關(guān)協(xié)議元素的信息,如 MIME 類型、字符編碼等。這使它們成為通過 Internet 處理資源的重要組成部分。
  4. 有效載荷標頭: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 請求標頭的一些場景:

  1. 通過建立適當?shù)?HTTP 標頭來測試控制和/或測試不同的變體。
  2. 需要對 Web 應(yīng)用程序的不同方面甚至服務(wù)器邏輯進行徹底測試的情況。
  3. 由于 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? 項目中的頭請求。

  1. 使用像 ?REST Assured? 這樣的驅(qū)動程序/庫而不是 ?Selenium?。
  2. 使用反向代理,例如瀏覽器 ?mob-proxy? 或其他一些代理機制。
  3. 使用 ?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ā)送了?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 格式的標頭。有大量的代碼重復,這降低了代碼的可維護性。

如果我們在構(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 ?對象。代碼演練

  1. 早些時候,我們必須在每個請求標頭中傳遞令牌。現(xiàn)在,一旦我們在方法?authenticateUser()? 中收到令牌響應(yīng),我們就將它放入請求的同一個實例中。這使測試步驟的執(zhí)行能夠向前推進,而無需像之前那樣為每個請求添加令牌。這使得標頭可用于對服務(wù)器的后續(xù)調(diào)用。
  2. 現(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)中所做的:代碼演練

  1. 初始化 ?RequestHeaderChangeDemo ?類對象作為端點。
  2. ?BaseURL ?是在第一個方法(即?authorizedUser?)中傳遞的。
  3. 在方法?authorizedUser ?中,我們調(diào)用了?RequestHeaderChangeDemo ?類的構(gòu)造函數(shù)?authenticateUser?。
  4. 因此,后續(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)站一起使用。為了解決這個問題,我們可能會縮小兩種可能的方法:

  1. 向所有請求添加授權(quán)標頭,沒有條件或例外。
  2. 僅向滿足特定條件的請求添加標頭。

盡管我們不會解決標頭管理問題,但我們?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 自動化測試中一直運行良好。


0 人點贊