How to resolve the Socket hang-up issues in webdriverIO

Some hacks to avoid socket hang-up issues when using Sauce labs with webdriverIO

Usman Ghani
Published in
6 min readMar 22, 2023

--

In automation, what we often ignore as QA Automation Engineers is time complexity. Even for a simple task, we will try to develop a complex logic and although it will work individually when we run the test in parallel it will throw random errors. The socket hang-up issue is one of those issues.

Why does the socket hang-up issue occur?

It occurs either when a client sends a request to a distant server but the response is not received in a timely manner or when you start a new command even though the previous command was not executed completely. In webdriverIO and sauce labs, there could be two reasons for the issue: code complexity or the sauce lab tunnel not being in the same region as the application under testing. Code complexity arises from excessive database queries or inefficient code.

Debugging is twice as hard as writing the code in the first place.

Brian Kernighan

As mentioned earlier when we developed the logic and tested our scenarios no errors appeared. The problem started when multiple feature files were getting executed in parallel on sauce labs. At the first sight, we were baffled because not only were we unaware of the root cause of the problem but also there was no support on the online platforms.

After skimming hundreds of articles, one idea struck my subconscious mind which was to sneak through our tasks, assertions and action files. We have followed the page object model in our test automation suite, thus functions are reusable. There I realized that calling the same function at numerous places caused excessive DB calls and use of maps which is causing the issue.

The first thing that came to my mind was (just kidding)

We finally got to the starting point of our debugging cycle. I went through different feature files, and functions, reduced time complexity and many things to successfully remove the socket hang-up issue from the code base.

In the below section, I will discuss all the hacks which I tried to resolve the socket hang-up issue.

Possible Solutions

These solutions are not only related to code or time complexity but also sauce lab tunnel. We will be deeply looking into these solutions.

Sauce labs Tunnel

It is a good practice to ensure that the sauce lab tunnel must be in the same region as the application under test. Moreover, that tunnel should be specifically designated to the automation team. The former part is to avoid latency between the calls from the client to the server and the latter part is to avoid unwanted traffic.

To set up a tunnel on a temporary basis, you will have to follow these steps:

  1. Download Sauce Connect Proxy from here.
  2. Extract the downloaded folder and then open the bin folder in it
Sauce Connect Poxy bin folder

3. Open cmd in the folder

4. Type this command in the cmd prompt for:

i) Non-shared tunnel

sc -u <sauceLabUsername> -k <sauceLabKey> --region <region> --tunnel-identifier <tunnelName> --no-remove-colliding-tunnels

ii) Shared Tunnel

sc -u <sauceLabUsername> -k <sauceLabKey> --region <region> --tunnel-identifier <tunnelName> --no-remove-colliding-tunnels --shared-tunnel

Note: Once you close the terminal or quit the command the tunnel will get terminated automatically.

Optimize Usage of Timeouts and Interval

Setting timeouts in the webdriverIO configuration file can be very effective. Those timeouts work globally in the code base where you use “wait for” commands. To wait for a UI element to appear particularly after a time, you can overwrite the timeout value for that element. One of the good practices would be to create a waits.json file like below:

waits.json

Don’t use an excessive amount of waits as it will increase your test suite execution time. If there are new changes in the environment i.e selector changes so it will take a lot of time to throw an error and may result in a socket hang-up issue.

When you use waitForDisplayed, waitForEnabled and waitForExist commands for dynamic waits there is a parameter named interval whose default value is 500 ms. For example,

Interval

This interval will send a post-call to the server to check for the element state after 500 ms. This will increase the number of commands per flow resulting in socket hang-up issues.

You can set waitForInterval to 15ms — 20ms in the webdriverIO configuration file below waitForTimeout like this:

waitForTimeout: maxTimeout,
waitForInterval: intermediateWait,

Optimized Use of Maps

In webdriverIO, there are maps that can be used to extract text, HTML and attributes of a list of elements using these commands:

i) Get text:

await $$(selector).map((ele)=>{ele.getText()})

ii) Get HTML:

await $$(selector).map((ele)=>{ele.getHTML(false)})

ii) Get Attribute:

await $$(selector).map((ele)=>{ele.getAttribute(attr)})

Maps are generally used to find a certain element within an array. For example, if you want to verify if a specific episode title is present within a carousel of episode titles. The first thing that can come to your mind is using maps, but it is not a good approach.

What can come in handy in this case is either isExisting() or isDisplayed(). You can make a dynamic or parameterized selector and use the above-mentioned commands on it. For example,

let existenceState: boolean = await $(`selectorFunction(selectorName)`).isExisting();
let displayState: boolean = await $(`selectorFunction(selectorName)`).isDisplayed();

Optimized DB Usage

If your test automation suite involves DB assertions, you need to minimize the number of DB calls.

For example, you have a query to get the data of an event and then you use the data from that query for multiple verifications. Don’t use that query in every assertion function as it will be an ineffective technique. Just introduce a global variable on top of your step definition file and store the data from the query in it. Later use that variable at multiple places where you want to verify the Db entry on UI.

Appropriate Number of Instances

In webdriverIO, the number of instances that can be opened at a time can be set in the configuration file. Use a manageable number, good practice is to set a number between 3, 4 and 5.

Appropriate Number of Refresh

Waiting for some data to update on the UI we can use the WebdriverIO refresh command:

await browser.refresh();
await browser.refresh();
await browser.refresh();
await browser.refresh();
await browser.refresh();

Calling too many refresh commands can result in socket hang-up issues too as there will be many incomplete commands causing latency in the response. Refresh can come in very handy but over-utilizing it might not be a good practice. What you can do is:

await browser.refresh()
await browser.pause(intermediateWait) // 10000
await browser.refresh()
await browser.pause(intermediateWait) // 10000
await browser.refresh()
await browser.pause(intermediateWait) // 10000

The above snippet will not only refresh the page but will also wait for the page to get stable thus no latency or incomplete response.

Crux

Time complexity plays an important role in coding. You must keep in mind that coding is not just integrating chunks of code, it's storytelling. Every variable is a character and each feature is a chapter including many scenarios. In case of any query or suggestion, feel free to reach out to me via Email or LinkedIn, or Instagram.

Happy Coding!

--

--

Writer for

SDET-1 & QAE @Emumba | JS | TS | Jenkins | Sauce labs | WebDriverIO | Cypress | Cucumber | Locust | JMeter| https://www.linkedin.com/in/usmanghani786/