自动化测试中BrowserStack空闲超时问题求助(已尝试官方方案)
BrowserStack启动URL前触发空闲超时错误的排查与解决
问题背景
在启动测试URL前,出现BrowserStack空闲超时错误,已尝试官方超时延长方案,但问题仍未解决。
测试代码
public class BrowserStackTestclass { public RemoteWebDriver driver; private Local l; public String sessionId; private String methodName; private DesiredCapabilities capabilities; public String username; public String accessKey; private static JSONObject jsonObject; public String browserName; @BeforeEach public void setUp(TestInfo testInfo) throws Exception { Map<String, String> env = System.getenv(); for (Map.Entry<String, String> entry : env.entrySet()) { System.out.println(entry.getKey() + " : " + entry.getValue()); } List<Integer> taskIDs = new ArrayList<Integer>(); String config = System.getProperty("config"); if (StringUtils.isBlank(config)) { config = "chrome.json"; } else { config = config + ".json"; } System.out.println("Using config as " + config); browserName = config; JSONParser parser = new JSONParser(); jsonObject = (JSONObject) parser.parse(new FileReader("src/test/resources/conf/" + config)); JSONArray envs = (JSONArray) jsonObject.get("environments"); DesiredCapabilities capabilities = new DesiredCapabilities(); Map<String, String> envCapabilities = (Map<String, String>) envs.get(0); Iterator it = envCapabilities.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); capabilities.setCapability(pair.getKey().toString(), pair.getValue().toString()); } Map<String, String> commonCapabilities = (Map<String, String>) jsonObject.get("capabilities"); it = commonCapabilities.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); if (capabilities.getCapability(pair.getKey().toString()) == null) { capabilities.setCapability(pair.getKey().toString(), pair.getValue().toString()); } } username = System.getProperty("BROWSERSTACK_USERNAME"); if (StringUtils.isBlank(username)) { username = (String) jsonObject.get("user"); } if (StringUtils.isBlank(username)) { username = System.getenv("BROWSERSTACK_USERNAME"); } accessKey = System.getProperty("BROWSERSTACK_ACCESS_KEY"); if (StringUtils.isBlank(accessKey)) { accessKey = (String) jsonObject.get("key"); } if (StringUtils.isBlank(accessKey)) { accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY"); } String localIdentifier=UUID.randomUUID().toString(); l = new Local(); Map<String, String> options = new HashMap<String, String>(); options.put("key", accessKey); capabilities.setCapability("browserstack.local", "true"); if(System.getProperty("env", "qa").equalsIgnoreCase("qa")) { options.put("forcelocal", "true"); options.put("localIdentifier", localIdentifier); capabilities.setCapability("browserstack.local", "true"); l.start(options); } // Set Default Capabilities capabilities.setCapability("project", "ProjectName"); capabilities.setCapability("browserstack.debug", "false"); capabilities.setCapability("browserstack.safari.enablePopups", "true"); capabilities.setCapability("browserstack.ie.enablePopups", "true"); capabilities.setCapability("browserstack.video", "true"); capabilities.setCapability("browserstack.console", "info"); capabilities.setCapability("browserstack.networkLogs", "true"); capabilities.setCapability("browserstack.idleTimeout", "240"); capabilities.setCapability("acceptSslCerts", "true"); capabilities.setCapability("browserstack.selenium_version", "3.141.59"); capabilities.setCapability("browserstack.localIdentifier", localIdentifier); // capabilities.setCapability("browserstack.sendKeys", "true"); // Set OS String os = getCapabilityByProperty("browser_os", "", capabilities); if (StringUtils.isNotBlank(os)) { capabilities.setCapability("browser_os", os); } // Set OS VERSION String osversion = getCapabilityByProperty("os_version", "", capabilities); if (StringUtils.isNotBlank(osversion)) { capabilities.setCapability("os_version", osversion); } // Set browser String browser = getCapabilityByProperty("browser", "", capabilities); if (StringUtils.isNotBlank(browser)) { capabilities.setCapability("browser", browser); } // Set browser version String browserversion = getCapabilityByProperty("browser_version", "", capabilities); if (StringUtils.isNotBlank(browserversion)) { capabilities.setCapability("browser_version", browserversion); } // Set device -used for mobile device String device = getCapabilityByProperty("device", "", capabilities); if (StringUtils.isNotBlank(device)) { capabilities.setCapability("device", device); } String buildNumber = System.getProperty("BUILD_NUMBER"); // Set Build Name {{env}}-build- String testSuite = System.getProperty("TESTSUITE"); if (StringUtils.isNotBlank(testSuite)) { capabilities.setCapability("build", StringUtils.defaultString(buildNumber, "Local") + "-" + convertToPascal(System.getProperty("env", "local")) + "-" + config + "-" + convertToPascal(testSuite)); } else { capabilities.setCapability("build", "Local Test Run"); } methodName = testInfo.getDisplayName(); // Set Test case name capabilities.setCapability("name", convertToPascal(getMethodName())); System.out.println("Url for browserstack " +"https://" + username + ":" + accessKey + "@" + jsonObject.get("server") + "/wd/hub"); driver = new RemoteWebDriver( new URL("https://" + username + ":" + accessKey + "@" + jsonObject.get("server") + "/wd/hub"), capabilities); driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS); sessionId = driver.getSessionId().toString(); System.out.println("Session id is "+sessionId); if (StringUtils.isBlank(sessionId)) { throw new RuntimeException("SessionId is blank"); } WebDriverRunner.setWebDriver(driver); } private static String getCapabilityByProperty(String key, String defaultValue, Capabilities capabilities) { String value = System.getProperty(key); if (StringUtils.isBlank(value)) { value = (String) capabilities.getCapability(key); if (StringUtils.isBlank(value)) { value = defaultValue; } } return value; } public DesiredCapabilities getCapabilities() { return capabilities; } public void setCapabilities(DesiredCapabilities capabilities) { this.capabilities = capabilities; } @AfterEach public void tearDown() throws Exception { driver.quit(); if (l != null) l.stop(); } public String getUsername() { return username; } public String getAcccessKey() { return accessKey; } public String getSessionId() { return sessionId; } public String getMethodName() { return methodName.replaceAll("[()]",""); } public static void mark(String username, String accessKey, String sessionId, String status, String reason) throws URISyntaxException, UnsupportedEncodingException, IOException { URI uri = new URI("https://" + username + ":" + accessKey + "@api.browserstack.com/automate/sessions/" + sessionId + ".json"); HttpPut putRequest = new HttpPut(uri); ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(); nameValuePairs.add((new BasicNameValuePair("status", status))); nameValuePairs.add((new BasicNameValuePair("reason", reason))); putRequest.setEntity(new UrlEncodedFormEntity(nameValuePairs)); HttpClientBuilder.create().build().execute(putRequest); } public static String convertToPascal(String word) { return StringUtils .capitalize(StringUtils.join(StringUtils.splitByCharacterTypeCamelCase(word), StringUtils.SPACE)); } }
问题排查与解决方案
1. 核心问题点定位
超时发生在RemoteWebDriver实例化前,说明BrowserStack会话创建后,初始化流程中存在长时间无交互的窗口,导致触发空闲超时。
2. 针对性修复方案
修正IdleTimeout参数类型:当前代码中
browserstack.idleTimeout传入的是字符串"240",该参数要求为数值类型,需修改为:capabilities.setCapability("browserstack.idleTimeout", 240);等待Local服务就绪:QA环境下启动BrowserStack Local服务后,未等待服务完全就绪就继续执行后续逻辑,添加等待逻辑:
if(System.getProperty("env", "qa").equalsIgnoreCase("qa")) { options.put("forcelocal", "true"); options.put("localIdentifier", localIdentifier); capabilities.setCapability("browserstack.local", "true"); l.start(options); // 等待Local服务启动完成 while(!l.isRunning()) { Thread.sleep(1000); } }移除冗余操作:代码中遍历打印所有环境变量,在环境变量较多时会消耗大量时间,直接移除该段日志逻辑,减少初始化耗时。
优化配置读取流程:将JSON配置文件的读取逻辑提前到测试类初始化阶段,避免每个测试方法重复读取,减少会话空闲时间。
3. 额外优化建议
- 会话创建后立即执行页面交互(例如打开测试站点),避免会话处于空闲状态;
- 检查JSON配置文件中的
environments和capabilities是否存在冗余配置,简化初始化步骤; - 若超时问题仍存在,可尝试将
browserstack.idleTimeout值调高至300(最大允许值)。
内容的提问来源于stack exchange,提问作者kavya M
相关产品推荐
相关产品推荐

