自动化测试教程:在JAVA中使用Selenium WebDriver修改HTTP请求头

1,965 阅读13分钟

如何使用Selenium WebDriver修改JAVA中的HTTP请求头?

最常见的测试自动化挑战之一是我们如何在Selenium WebDriver中修改请求头信息。作为一个自动化测试人员,你会遇到任何编程语言的挑战,包括Java。在解决问题之前,我们需要更好地理解问题陈述,并在使用Selenium WebDriver时得出不同的可能性来修改Java中的请求头。

在这个Selenium Java教程中,我们将学习如何使用Selenium WebDriver在Java中修改HTTP请求头的不同可用选项。

所以,让我们开始吧!

什么是HTTP头文件

HTTP头是HTTP协议的一个重要组成部分。它们定义了一个HTTP消息(请求或响应),并允许客户端和服务器与该消息交换可选的元数据。它们由一个不区分大小写的头域名称和一个冒号组成,然后是一个头域值。标题字段可以扩展到多行,方法是在每一行前面至少加一个空格或水平制表符。

头信息可以根据其上下文进行分组。

  1. 请求标头。HTTP请求头用于提供关于正在获取的资源和提出请求的客户端的额外信息。
  2. 响应标头。HTTP响应头提供关于响应的信息。位置头指定了资源的位置,而服务器头则提供了关于提供资源的服务器的信息。
  3. 代表性头信息。HTTP表示头是任何HTTP响应的一个重要部分。它们提供关于协议元素的信息,如mime类型、字符编码等等。这使得它们成为在互联网上处理资源的一个重要部分。
  4. 有效载荷头。HTTP有效载荷头包含有关HTTP消息有效载荷的数据(如其长度和编码),但与表示无关。

深入探究HTTP请求头

HTTP请求头是一种通信机制,使浏览器或客户能够从(网络)服务器上请求特定的网页或数据。在网络通信或互联网浏览中使用时,HTTP请求头使浏览器和客户端能够通过发送请求与相应的网络服务器通信。

HTTP请求头描述了网络浏览器为加载页面而发送的请求。它也被称为客户端到服务器的协议。标头包括客户端请求的细节,如用户使用的浏览器和操作系统的类型,以及在屏幕上正确显示请求内容所需的其他参数。

以下是HTTP请求头中包含的主要信息。

  • IP地址(源)和端口号。
  • 请求的网页的URL。
  • 网络服务器或目标网站(主机)。
  • 浏览器将接受的数据类型(文本、html、xml等)。
  • 浏览器类型(Mozilla、Chrome、IE),以发送兼容数据。

作为回应,一个包含请求数据的HTTP响应头被发回。

改变HTTP请求头的必要性

你能猜到为什么一旦请求头已经被设置到脚本中,我们还需要改变它吗?

下面是一些你可能需要改变HTTP请求头的情况。

  1. 测试控制和/或通过建立适当的HTTP头来测试不同的变体。
  2. 当网络应用程序的不同方面甚至是服务器逻辑都需要彻底测试时,需要测试案例。
  3. 由于HTTP请求头用于启用网络应用程序逻辑的某些特定部分,而这些部分在正常模式下通常是禁用的,因此可能需要根据测试情况不时地修改HTTP请求头。

在被测试的网络应用程序上测试访客模式是最理想的情况,你可能需要修改HTTP请求头。

然而,Selenium RC曾经支持的修改HTTP请求头的功能,现在却不被Selenium Webdriver处理。

这就是为什么会出现这样的问题:当测试自动化项目是用Selenium框架和Java编写时,我们如何改变请求头。

关于使用Java、Selenium、JUnit 5和Maven从头开始开发Selenium测试自动化的快速概述,请看下面LambdaTest YouTube频道的视频。

如何在Selenium Java项目中修改头请求

在Selenium Java教程的这一部分,我们看看在Java中修改头请求的众多方法。大体上,有几种可能性,按照这些可能性可以在Java-Selenium项目中修改头请求。

  1. 使用像REST Assured这样的驱动/库,而不是Selenium。
  2. 使用反向代理,如浏览器mob-proxy或其他一些代理机制。
  3. 使用Firefox浏览器扩展,这将有助于修改请求的头文件。

让我们逐一探讨每种可能性。

使用REST保证库修改HTTP请求头信息

除了Selenium,我们还可以使用REST Assured,它是一个很好的工具,可以用简单的方式来处理REST服务。

在任何IDE(如Eclipse)中,将REST Assured与你的项目进行配置的前提条件相当简单。在设置了Java、Eclipse和TestNG之后,你需要下载所需的REST Assured jar文件

阅读 -如何在Eclipse中安装TestNG。一步一步的指南

下载完jar文件后,你必须在Eclipse中创建一个项目,并将下载的jar文件作为外部jar添加到属性部分。这也类似于我们将Selenium jar文件添加到项目中的方式。一旦你成功地设置了带有REST Assured库的Java项目,你就可以开始了。

我们打算创建一个机制,使请求头可被定制。为了实现上述的可能性,我们首先需要知道创建请求头的常规方法。

让我们考虑以下情况。

  • 我们有一个名为RequestHeaderChangeDemo的Java类,在这里我们维护基本配置
  • 我们有一个名为TestSteps的测试步骤文件,在这里我们将调用RequestHeaderChangeDemo Java类中的方法,通过它我们将执行我们的测试。

观察下面这个名为RequestHeaderChangeDemo的Java类。

BASE_URL是亚马逊网站(https://www.amazon.com),在这个网站上应用了以下四个方法。

  • authenticateUser
  • 获取产品
  • 添加产品
  • 删除产品
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类文件中,我们在每个连续的方法中都重复发送了BASE_URL和头文件。例子如下所示。

1
2
3
4
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中的实例方法属于类的对象,而不是类本身,所以即使在创建了类的对象之后,该方法也可以被调用。与此同时,我们还将重写实例方法。

将该方法转换为实例方法会带来以下好处。

  • 认证只在一个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);
    }
 
    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());
    }
}

代码演练

  1. 我们已经创建了一个构造函数来初始化包含BaseURL和Request Headers的RequestSpecification对象。
  2. 早些时候,我们必须在每个请求头中传递令牌。现在,一旦我们在方法authenticateUser()中收到tokenresponse,我们就把它放到同一个请求实例中。这使得测试步骤的执行能够向前推进,而不像先前那样为每个请求添加令牌。这使得头可以用于随后对服务器的调用。
  3. 这个RequestHeaderChangeDemo Java类现在将在TestSteps文件中被初始化。

我们根据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());
    }
}

代码演练

下面是我们在修改后的实现中所做的工作。

  1. 将RequestHeaderChangeDemo类对象初始化为端点。
  2. BaseURL被传递到第一个方法中(即authorizedUser)。
  3. 在authorizedUser方法中,我们调用了RequestHeaderChangeDemo类的构造函数authenticateUser。
  4. 因此,相同的端点对象被随后的步骤定义所使用。

使用反向代理修改HTTP请求头,如浏览器Mob-Proxy

顾名思义,在处理Java-Selenium自动化测试套件中的请求头变化时,我们可以选择使用代理。由于Selenium禁止在浏览器和服务器之间注入信息,代理可以起到救援作用。

如果测试是在企业防火墙后面进行的,这种方法并不可取。

作为一个网络基础设施组件,代理通过在客户端和服务器之间定位,使网络流量通过它。在企业界,代理的工作原理类似,使流量通过它,允许安全的流量,阻止潜在的威胁。代理机构具有修改请求和响应的能力,可以是部分的也可以是完全的。

其核心思想是发送授权头,绕过包括证书对话的阶段,也被称为基本认证对话。然而,这被证明是一个累人的过程,特别是当测试案例需要频繁地重新配置时。

这就是浏览器mob-proxy库出现的地方。当你使代理配置成为Selenium自动化测试套件的一部分时,代理配置在你每次执行测试套件时都会有效。

让我们看看我们如何使用浏览器mob-proxy与一个有基本认证的样本网站。为了解决这个问题,我们可以缩小两个可能的方法。

  1. 在所有的请求中添加授权头信息,没有条件或例外。
  2. 只对符合某些条件的请求添加头信息。

虽然我们不会解决头信息管理问题,但我们仍然会展示如何在浏览器mob-proxy授权工具集的帮助下解决授权问题。

在Selenium Java教程的这一部分,我们将只关注第一种方法(即为所有的请求添加授权头信息)。

首先,我们在pom.xml中添加browsermob-proxy的依赖项

如果你想把这种方法传递给所有的头信息请求,一个特定的代理,在这种情况下,forAllProxy方法应该被调用,如下图所示。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
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 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(
    {
    }
}

在上面的代码中,以String authHeader开头的那一行说明我们正在创建头信息,这将被添加到请求中。之后,这些请求将通过我们在proxy.addHeader("checkauth", authfirstHeader)中创建的代理传递。

01
02
03
04
05
06
07
08
09
10
11
12
try {
String authHeader ="Basic " + Base64.getEncoder().encodeToString("webelement:click".getBytes("utf-8"));
proxy.addHeader("checkauth", authfirstHeader);
}
catch (UnsupportedEncodingException e)
{
………………………………………………………………………………
………………………………………………………………………………
……………………………………………………………………………...
}
proxy.start(0);
}

最终,我们启动代理设置0来标记开始参数,代理在端口上启动。

使用Firefox扩展修改HTTP请求头

在Selenium Java教程的这一部分,我们看一下如何使用适当的Firefox浏览器扩展来修改请求头。这个选项的主要缺点是,它只适用于Firefox(而不是其他浏览器,如Chrome、Edge等)。

执行以下步骤,使用火狐浏览器扩展修改HTTP请求头。

  • 下载火狐浏览器扩展
  • 加载该扩展。
  • 设置扩展的首选项。
  • 设置期望的能力。
  • 准备好测试自动化脚本。

让我们逐一浏览每个步骤。

1.下载火狐浏览器扩展

搜索带有.*xpi的火狐浏览器扩展,并将其设置在项目中。

2.加载火狐浏览器扩展

参照下面的代码添加Firefox配置文件。

01
02
03
04
05
06
07
08
09
10
11
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();
}

3.设置扩展的首选项

一旦我们将Firefox扩展加载到项目中,我们就设置偏好(即在扩展被触发前需要设置的各种输入)。这是用profile.setPreference方法完成的。

这个方法通过密钥设置参数机制为任何给定的配置文件设置偏好。这里的第一个参数是设置值的键,此外还有第二个参数,它设置了一个相应的整数值。

下面是参考实现。

1
2
3
4
5
6
7
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);

在上面的代码中,我们列出了我们要设置头的实例的次数。

1
profile.setPreference("modifyheaders.headers.count",1);

接下来,我们指定动作,头名称和头值包含从API调用中动态接收的值。

1
profile.setPreference("modifyheaders.headers.action0","Add");

对于实现.setPreference的其余行,我们启用所有,这样就可以在WebDriver实例化Firefox浏览器时,让扩展程序被加载,同时用HTTP头将扩展程序设置为活动模式。

4.设置期望的能力

Selenium中的 "期望能力 "用于设置需要进行自动化测试的浏览器、浏览器版本和平台类型。

下面是我们如何设置期望的能力。

1
2
3
4
5
6
7
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,最大的基于云的自动化测试平台,提供更快的跨浏览器测试基础设施的地方。

通过LambdaTest,你可以灵活地修改不同浏览器和平台组合的HTTP请求头。如果你愿意使用Firefox扩展来修改HTTP请求头,你可以使用LambdaTest在不同版本的Firefox浏览器上实现同样的功能。

5.起草整个测试自动化脚本

一旦你完成了上述所有步骤,我们就着手设计整个测试自动化脚本。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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");
}

阅读 -使用Selenium和Java的浏览器自动化

总结

在这个Selenium Java教程中,我们探讨了处理HTTP请求头的修改的三种不同方法。Selenium本身就是一个很好的工具,在网络自动化测试中一直表现良好。

来源

然而,该工具不能改变请求头。在探索了所有三种在Java Selenium项目中修改请求头的替代方案后,我们可以为使用REST Assured的第一个方案担保。然而,你可能想尝试一下其他选项,并在评论区提出你的观察和看法。

经LambdaTest许可,发表在Java Code Geeks上,LambdaTest是我们JCG项目的合作伙伴。点击这里查看原文。如何使用Selenium WebDriver修改JAVA中的HTTP请求头?

Java Code Geeks的撰稿人所表达的观点属于他们自己。