Modify server IP address in Selenium Java tests with BrowserMob-Proxy

It might happen (when you develop automated tests with Selenium) that you will need to have the actual servers your page loads the resources from to be different from what is returned by DNS servers. This is what you can achieve easily with BrowserMob-Proxy library (especially if you develop your tests in Java).

A bit of theory

When you fetch a resource by domain name, the domain name is eventually translated to IP address so that the packets which carry your request are able to find the proper route to the destination server. This process is called "resolution". Normally the client takes the IP address for a domain from DNS server.

One domain name can be resolved to several IP addresses so that different calls to a resource are handled by different servers (this is one of the ways how you can load balance your system)

Why do I need to change IP address?

Sometimes you might need to take a resource from different server rather than the server returned by DNS. For example you could need to test a page with different version of JavaScript library. So by changing the IP address to which a domain from that library URI is resolved you can make your page load different library without the need to change page code (considering the aspects of resource caching).

Another reason is when your server under test has moved to different IP but DNS servers have not get their mappings updated yet. Since such the update might take some amount of time, you could locally resolve particular domain to a required IP address.

How do I change IP address of a domain?

Some people use OS mechanisms to overwrite the domain-to-ip mappings in hosts file. The downside of this approach is that usually in order to modify such file you have to obtain administrator privileges. Another thing is that such mapping is persistent so that you will have to take care of having that up-to-date, remove it when it is no longer required, and do not run other tests in parallel with yours if they could be impacted by changed mapping.

Another, more flexible way, is to use BrowserMob-Proxy library which allows to embed the proxy into your Java code, configure it, start it and stop it right from your test. Below I’m going to show you one example of how you can fake domain IP in your Selenium test in runtime.

Sample code

In my example I’m overriding the IP address for one particular domain. Such approach more fits the use cases I described above. I’m not using any unit test framework here to be more concentrated on the posts’s topic.

public static void main(String[] args) {
    WebDriver driver = null;
    BrowserMobProxyServer browserMobProxy = null;
    try{
        // Create instance of our proxy
        browserMobProxy = new BrowserMobProxyServer();
        // Override domain resolution for webelement.click domain
        // So that it points to localhost
        browserMobProxy.setHostNameResolver(new NativeResolver(){
            @Override
            public Collection<InetAddress> resolve(String originalHost) {
                if("webelement.click".equals(originalHost)){
                    try {
                        return Arrays
                                .asList(
                                        new InetAddress[]{
                                                InetAddress.getByName("127.0.0.1")
                                        });
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                }
                return super.resolve(originalHost);
            }
        });
        // Start proxy on random port
        browserMobProxy.start(0);
        // Convert proxy to Selenium Proxy object
        Proxy proxy = ClientUtil.createSeleniumProxy(browserMobProxy);
        // Start Chrome with our custom proxy
        System.setProperty("webdriver.chrome.driver", "/path_to_your_driver/chromedriver");
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.setProxy(proxy);
        chromeOptions.setAcceptInsecureCerts(true);
        driver = new ChromeDriver(chromeOptions);
        // Do some test
        // ...
    }finally {
        if (driver != null) {
            driver.quit();
        }
        if(browserMobProxy != null && !browserMobProxy.isStopped()){
            browserMobProxy.stop();
        }
    }
}

The idea is that you run and stop the proxy straight from your test code. You configure it in your test code and you make your browser use that just started and configured proxy server.

This makes your tests more isolated from the environment so that no other tests have the risk to be impacted and no other tests can impact your ones.

What else to consider

Whatever approach to fake IP address you choose, you have to remember of HTTPs protocol. You might run into issues if the server with different IP would not be able to provide valid SSL certificate for the domain in your browser’s address bar or in src attribute of the resources loaded by your page.

This seems to be it. If you still have the questions please send them to me using this form. I will amend the article according to your feedback.