2017-04-26 48 views
0

我们的CircleCI测试使用硒,通过selenium-webdriver通过PhantomJS运行UI测试。测试中我们游民ENV工作100%的时间在本地,但未能约1开出3次对CircleCI与像ECONNREFUSED错误:CircleCI上的间歇硒ECONNREFUSED错误

Error: ECONNREFUSED connect ECONNREFUSED 10.0.4.1:59525 
     at ClientRequest.<anonymous> (node_modules/selenium-webdriver/http/index.js:238:15) 
     at Socket.socketErrorListener (_http_client.js:310:9) 
     at emitErrorNT (net.js:1278:8) 
     at _combinedTickCallback (internal/process/next_tick.js:74:11) 
     at process._tickCallback (internal/process/next_tick.js:98:9) 
    From: Task: WebDriver.navigate().to(http://127.0.0.1:8080/src/public/login.php?t=ur&ign=1&ut=ad) 
     at thenableWebDriverProxy.schedule (node_modules/selenium-webdriver/lib/webdriver.js:816:17) 
     at Navigation.to (node_modules/selenium-webdriver/lib/webdriver.js:1140:25) 
     at thenableWebDriverProxy.get (node_modules/selenium-webdriver/lib/webdriver.js:997:28) 
     at User.logIn (testJs/ui/utils/user.js:9:16) 
     at Host.logInAsHost (testJs/ui/utils/host.js:13:14) 
     at Preferences.disableRevenueGeneration (testJs/ui/utils/preferences.js:57:14) 
     at Context.<anonymous> (testJs/ui/tests/preferences.js:13:22) 

这些错误发生在整个测试中随机时间,他们没有被由我们测试中的任何特定地点触发。据我所知,这个问题发生在selenium/selenium-webdriver端,因为web服务器保持运行并且不会产生任何错误。

我已经尝试了所有的以下,其中没有一个已经工作:

  1. 升级到最新的硒的webdriver(3.4.0)
  2. 升级到较新版本的的NodeJS(6.9 0.2)
  3. 使用不同的Web服务器
  4. 升级到使用最新版本PhantomJS(1.9.7-15)
  5. 的尝试export DBUS_SESSION_BUS_ADDRESShttps://github.com/SeleniumHQ/docker-selenium/issues/87
  6. 在node_modules/selenium-webdriver/http/index.js中,我修改了代码以重试ECONNREFUSED错误,方法是重新使用ECONNRESET中已有的重试,即if (e.code === 'ECONNRESET') {变为if (e.code === 'ECONNRESET' || e.code === 'ECONNREFUSED') {。这不起作用,因为那时selenium-webdriver只是无限期地重试,并且从中我意识到问题似乎是一旦遇到ECONNREFUSED错误,硒/硒驱动程序是不可恢复的。

有没有人找到解决方案?

回答

0

警告:这仅仅是一个破解,绝不是一个合适的解决方案,但它是唯一一个在CircleCI上可靠地为我们工作的人。希望别人能找到更好的解决方案。

我们的测试框架,摩卡,似乎有一个专门为硒开发的重试机制(去图):http://mochajs.org/#retry-tests。第一个问题是afterbefore中的代码未被重试。因此,您需要将代码移至beforeEachafterEach。第二个问题是,您需要在beforeEachafterEach中使用一些特殊逻辑来处理驱动程序错误。

因此,为了利用这种机制,我们不得不重构我们的测试,使每个测试有一个beforeEach,设置了一个新的webdriver的实例,然后一个afterEach是退出该驱动程序实例。这样,一个崩溃的驱动程序实例不会停止所有后续测试。我们开发了一个辅助函数,我们在所有describe块的开头调用,以便我们不必为测试添加太多的代码。

然后,我们将this.retries(10)添加到我们最顶端的describe区块。

这里是辅助类:

var webdriver = require('selenium-webdriver'); 

// We default to using phantomjs, but in the future, when we run tests in other browsers, e.g. via 
// SauceLabs, we'll want to change the browser. 
var customPhantom = webdriver.Capabilities.phantomjs(); 
customPhantom.set('ssl-protocol', 'any'); 

// For convenience in tests 
global.By = webdriver.By; 
global.until = webdriver.until; 

var SeleniumStabilizer = function() {}; 

SeleniumStabilizer.prototype.MAX_RETRIES = 10; 

SeleniumStabilizer.prototype.createDriver = function() { 
    global.driver = new webdriver.Builder() 
     // .forBrowser('phantomjs') 
     .withCapabilities(customPhantom) 
     .build(); 

    // Resolves when driver is ready 
    return global.driver; 
}; 

SeleniumStabilizer.prototype.init = function (opts) { 

    var self = this, 
     n = 0; 

    var beforeEachWithRetries = function() { 
     // Create a fresh selenium driver for the next test 
     return self.createDriver().then(function() { 
      if (opts.onBeforeEach) { 
       // Execute the test-specific defined onBeforeEach 
       return opts.onBeforeEach(); 
      } 
     }).catch(function (err) { 
      // ECONNREFUSED error and we should retry? 
      if (err.message.indexOf('ECONNREFUSED') !== -1 && n++ < self.MAX_RETRIES) { 
       return beforeEachWithRetries(); 
      } else { 
       throw err;    
      } 
     }); 
    }; 

    opts.beforeEach(function() { 
     n = 0; 
     return beforeEachWithRetries(); 
    }); 

    var afterEachWithRetries = function() { 
     return Promise.resolve().then(function() { 
      if (opts.onAfterEach) { 
       // Execute the test-specific defined onAfterEach 
       return opts.onAfterEach(); 
      } 
     }).then(function() { 
      // Quit the selenium driver before starting the next test 
      return driver.quit(); 
     }).catch(function (err) { 
      // ECONNREFUSED error and we should retry? 
      if (err.message.indexOf('ECONNREFUSED') !== -1 && n++ < self.MAX_RETRIES) { 
       // Create a new driver 
       return self.createDriver().then(function() { 
        return afterEachWithRetries(); 
       }); 
      } else { 
       throw err;    
      } 
     }); 
    }; 

    opts.afterEach(function() { 
     n = 0; 
     return afterEachWithRetries(); 
    }); 
}; 

然后,你的测试看起来像:

var seleniumStabilizer = new SeleniumStabilizer(); 

describe('my tests', function() { 

    this.retries(seleniumStabilizer.MAX_RETRIES); 

    describe('search engines', function() { 

    seleniumStabilizer.init({ 
     beforeEach: beforeEach, 
     afterEach: afterEach, 
     onBeforeEach: function() { 
     // e.g. return doPromiseAfterDriverSetUp(); 
     }, 
     onAfterEach: function() { 
     // e.g. return doPromiseBeforeDriverQuits(); 
     } 
    }); 

    it('should get google', function() { 
     return driver.get('https://www.google.com'); 
    }); 

    it('should get amazon', function() { 
     return driver.get('https://www.amazon.com'); 
    }); 

    }); 

});