测试自动化挑战之一修改 Selenium WebDriver 中的请求请求头。我将分享如何使用 Selenium WebDriver 修改 HTTP 请求请求头。
HTTP 请求头是 HTTP 协议的重要组成部分。它们定义了 HTTP 消息(请求或响应)并允许客户端和服务器与消息交换可选的元数据。它们由不区分大小写的头字段名称后跟一个冒号,然后是头字段值组成。标题字段可以扩展到多行,方法是在每一额外行前至少有一个空格或水平制表符。
标题可以根据其上下文进行分组:
以下是 HTTP 请求请求头中包含的主要信息:
以下是测试工作中可能需要更改 HTTP 请求请求头的一些场景:
在被测 Web 应用程序上测试访客模式是测试人员可能需要修改 HTTP 请求请求头的情况。但是 Selenium RC 曾经支持的修改 HTTP 请求头的功能,现在 Selenium Webdriver 不处理了。
Selenium Java 中修改请求头请求的多种方法。大体上,有几种可能,接下来可以修改 Java-Selenium 项目中的头请求。
与 Selenium 一起,我们可以使用 REST Assured,它是一种以简单方式使用 REST 服务的绝佳工具。为项目配置 REST Assured 教程非常简单,这里就不介绍了。
让我们考虑以下场景:
观察下面名为 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 之后也可以调用该方法。与此同时,我们还将重写实例方法。
将方法转换为实例方法有以下优点:
因此,让我们看看当我们使用实例方法时 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);
}
此处省略部分代码
}
我们根据 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);
}
}
这是我们在修改后的实现中所做的:
顾名思义,在 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(而不适用于 Chrome、Edge 等其他浏览器),现在很少用 Firefox 做测试了,简单学习一下。
执行以下步骤以使用 Firefox 扩展修改 HTTP 请求请求头:
让我们一步一步来:
自行解决。
参考以下代码添加 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");
}