One of the test automation challenges is to modify the request headers in Selenium WebDriver. I will share how to modify HTTP request headers using Selenium WebDriver.

What is an HTTP request header

HTTP request headers are an important part of THE HTTP protocol. They define HTTP messages (requests or responses) and allow clients and servers to exchange optional metadata with messages. They consist of a case-insensitive header field name followed by a colon, followed by the header field value. A header field can extend to multiple lines by prefacing each additional line with at least one space or horizontal TAB.

Headings can be grouped according to their context:

  • Request headers: HTTP request headers are used to provide additional information about the resource being fetched and the client making the request.
  • Response header: The HTTP response header provides information about the response.

Here is the main information contained in the HTTP request header:

  • IP address (source) and port number.
  • The URL of the requested page.
  • Web server or target Web site (host).
  • The type of data the browser will accept (text, HTML, XML, and so on).
  • Type of browser that sends compatible data (Mozilla, Chrome, IE).
  • As a response, the HTTP response request header containing the request data is sent back.

The HTTP request header needs to be changed

Here are some scenarios in which you might need to change the HTTP request headers in a test effort:

  • Test control by establishing appropriate HTTP request headers, testing different versions.
  • Situations where different aspects of a Web application or even server logic need to be thoroughly tested.
  • Because HTTP request headers are used to enable specific parts of the Web application logic that are normally disabled in normal mode, it may be necessary to modify the HTTP request headers from time to time, depending on the test scenario.

Testing the visitor pattern on the Web application under test is a case where the tester might need to modify the HTTP request header. However, Selenium RC once supported the ability to modify HTTP request headers, which Selenium Webdriver now does not handle.

Selenium modifies the request header

Various ways to modify a request header request in Selenium Java. In general, there are several possibilities, and you can then modify the header request in a Java-Selenium project.

  • Use the Java HTTP request framework.
  • Use a reverse proxy.
  • Use the Firefox browser extension.

Java HTTP request framework

With Selenium, we can use REST Assured, which is a great tool for using REST services in a simple way. Configuring the REST Assured tutorial for your project is very simple and won’t be covered here.

Let’s consider the following scenario:

  • We have a Java class called RequestHeaderChangeDemo where we maintain the basic configuration
  • We have a test step file called TestSteps in which we will call the methods in the RequestHeaderChangeDemo Java class through which we will execute our tests.

Look at the following Java class called RequestHeaderChangeDemo.

BASE_URL is a site that applies the following four methods:

  • Authenticated user
  • To get the product
  • Add product
  • Remove the product
    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 newRestResponse(Token.class, response); } omit some duplicate code here}Copy the code

In the Java class file above, we repeatedly send BASE_URL and HEADERS in each successive method. Here is an example:

RestAssured.baseURI = BASE_URL;
RequestSpecification request = RestAssured.given(); 
request.header("Content-Type"."application/json");
Response response = request.body(authRequest).post(Route.generateToken());
Copy the code

The request.header method requests a request header in JSON format. There is a lot of code duplication, which reduces the maintainability of the code. This can be avoided if we initialize the RequestSpecification objects in the constructor and make these methods non-static (that is, create instance methods). Because instance methods in Java belong to the Object of a class rather than the class itself, the method can be called even after the Object of the class is created. At the same time, we will also rewrite the instance methods.

Converting methods to instance methods has the following advantages:

  • Authentication is only done once in a RequestSpecification object. It is no longer necessary to create the same request for other requests.
  • Be flexible with the request headers in your project.

So let’s look at the Java class RequestHeaderChangeDemo and the TestSteps file TestSteps when we use the instance methods.

Java class of the RequestHeaderChangeDemo class with instance methods

    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(a) {
            Response response = request.get(Route.products());
            return newRestResponse(Products.class, response); } Omit some code here}Copy the code

The code field

  • We created a constructor to initialize the RequestSpecification object that contains the BaseURL and request headers.
  • Earlier, we had to pass tokens in each request header. Now, once we receive the token response in the authenticateUser() method, we put it into the same instance of the request. This enables the execution of the test step to move forward without the need to add tokens for each request as previously. This makes the request header available for subsequent calls to the server.
  • The RequestHeaderChangeDemo Java class will now be initialized in the TestSteps file.

We change the TestSteps file based on the changes in the RequestHeaderChangeDemo Java class.

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(a)
    {
        endPoints = new RequestHeaderChangeDemo (BaseUrl);
        AuthorizationRequest authRequest = new AuthorizationRequest("(Username)"."(Password)");
        endPoints.authenticateUser(authRequest);
    }
 
    @Given("^Available Product List$")
    public void availableProductLists(a) 
    {       
        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 = newAddProductsRequest(USER_ID, code); userAccountResponse = endPoints.addProduct(addProductsRequest); }}Copy the code

Here’s what we did in our revised implementation:

  • Initialize the RequestHeaderChangeDemo class object as the endpoint.
  • BaseURL is passed in the first method, authorizedUser.
  • In the method authorizedUser, we call authenticateUser, the constructor of the RequestHeaderChangeDemo class.
  • Therefore, the next step definition uses the same endpoint object.

Using a Reverse proxy

As the name implies, we have the option of using a proxy when dealing with request header changes in the Java-Selenium automated test suite. Because Selenium prohibits the injection of information into browsers and servers, you can use a proxy for processing. This approach is not preferred if the test is being executed behind a corporate firewall.

As a Web infrastructure component, a proxy enables Web traffic to flow through it by positioning itself between a client and a server. The proxy works similarly, allowing traffic to pass through it, allowing safe traffic to pass through and blocking potential threats. The broker has the ability to partially or completely modify requests and responses.

The core idea is to send the authorization request header, bypassing the stage that contains the credentials dialog, also known as the basic authentication dialog. However, this turned out to be a tiring process, especially if the test cases required frequent reconfiguration.

This is where the browser mob-Proxy library comes in. Let’s see how the browser Mob-Proxy works with a sample web site that uses basic authentication protection. To solve this problem, we may narrow down two possible approaches:

  • Adds authorization request headers to all requests, without conditions or exceptions.
  • Add headers only to requests that meet certain criteria.

Although we won’t solve the request header management problem, we will demonstrate how to solve the authorization problem with the help of the browser mob-Proxy authorization tool set. In this part of the Selenium Java tutorial, we will show only the first approach (that is, adding authorization headers to all requests).

First we add a dependency on Browsermob-proxy in pop.xml

    <dependencies>
        <dependency>
            <groupId>net.lightbody.bmp</groupId>
            <artifactId>browsermob-core</artifactId>
            <version>2.1.5</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
Copy the code

Then you need to make some changes in the code:

public class caseFirstTest
{
    WebDriver driver;
    BrowserMobProxy proxy;
 
    @BeforeAll
    public static void globalSetup(a)
    {
        System.setProperty("webdriver.gecko.driver"."(path of the driver)");
    }
 
    @BeforeEach
    public void setUp(a)
    {
        setUpProxy();
        FirefoxOptions Options = new FirefoxOptions();
        Options.setProxy(ClientUtil.createSeleniumProxy(proxy));
        driver = new FirefoxDriver(Options);
    }
 
    @Test
    public void testBasicAuth(a)
    {
        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(a)
    {
        if(driver ! =null)
        {
            driver.quit();
        }
        if(proxy ! =null) { proxy.stop(); }}private void setUpProxy({}}Copy the code

If this method is to be passed to all header requests, i.e. specific proxies, in this case the forAllProxy method should be called as follows:

public void forAllProxy(a)
{
    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);
}
Copy the code

In the code above, the line starting with authHeader indicates that we are creating the request header, which will be added to the request. These requests are then delivered via a proxy we created in 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);
}
Copy the code

Finally, we start the agent by setting 0 to mark the start parameter, and the agent starts on the port.

Using Firefox Extensions

Here’s how to modify the request header request using the appropriate Firefox browser extension. The main downside to this option is that it only works with Firefox (not Chrome, Edge, etc.), and Firefox is rarely tested, so take a quick look.

Perform the following steps to modify the HTTP request header using the Firefox extension:

  • Download the Firefox browser extension
  • Load the extension.
  • Set extension preferences.
  • Set up the required functionality.
  • Prepare test automation scripts.

Let’s take it step by step:

Download the Firefox browser extension

Work it out.

Loading firefox Extensions

Refer to the following code to add a Firefox profile:

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();
}
Copy the code

Set extension preferences

Once we load the Firefox extension into the project, we set the preferences (that is, the various inputs that need to be set before the extension is triggered). This is done using the profile.setPreference method.

This method sets the preferences for any given configuration file through the keyset parameter mechanism. The first argument here is the key that sets the value, and the second argument sets the corresponding integer value.

This is the reference implementation:

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);
Copy the code

In the code above, we list the number of times we want to set the header instance.

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

Next, we specify the operation, request header name, and request header value to contain the values dynamically received from the API call.

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

For the rest of the implementation, we enable All so that it allows the extension to be loaded when WebDriver instantiates the Firefox browser and to be set to active mode using HTTP request headers.

Set up the required functionality

Desired Capabilities in Selenium is used to set the browser, browser version, and platform type that automated tests need to be performed.

Here’s how we set up the required functionality:

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");
Copy the code

Complete automation use cases

After completing all of the above steps, we will proceed to design the entire test automation script:

public void startwebsite(a)
{
    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");
}
Copy the code

Welcome to FunTester, Have Fun ~ Tester!

  • 140 interview questions (UI, Linux, MySQL, API, security)
  • Graphic HTTP brain map
  • Share a Fiddler study bag
  • Thread pools handle batch interface request practices
  • 100K QPS, K6, Gatling and FunTester showdown!
  • Performance bottleneck tuning
  • How do I choose an API test tool
  • Beginner’s API testing tips
  • API Automation Test Guide
  • API Testing Basics
  • There is no data to drive automated tests
  • Automated and manual testing to keep the balance!
  • 43 common software test categories