为我们的 CI 使用 Heroku 和 Codeship。一直在使用 Protractor 在 Angular 中创建一个网络应用程序进行测试。一切都在本地运行良好,但是当它到达 CI 时,一些测试将失败。奇怪的是,失败的是随机的:有时它们会失败,然后刷新构建使它们再次通过。这是错误代码:
Running "protractor:your_target" (protractor) task Starting selenium standalone server... [launcher] Running 1 instances of WebDriver Selenium standalone server started at http://10.0.3.170:40348/wd/hub .....F..F.... Failures: 1) Application Filter should sort stores by number 0-9 Message: Error: Index out of bound. Trying to access element at index: 0, but there are only 0 elements that match locator By.cssSelector(".name") Stacktrace: Error: Index out of bound. Trying to access element at index: 0, but there are only 0 elements that match locator By.cssSelector(".name") ==== async task ==== Asynchronous test function: it() Error at [object Object].<anonymous> (/home/rof/src/github.com/DigitalInnovation/staffdirectory/test/e2e/app.js:83:34) Error at [object Object].<anonymous> (/home/rof/src/github.com/DigitalInnovation/staffdirectory/test/e2e/app.js:76:9) at [object Object].<anonymous> (/home/rof/src/github.com/DigitalInnovation/staffdirectory/test/e2e/app.js:50:5) at Object.<anonymous> (/home/rof/src/github.com/DigitalInnovation/staffdirectory/test/e2e/app.js:3:1) 2) Application Store Name And Number should show 4 store numbers Message: Expected 0 to be 4. Stacktrace: Error: Failed expectation at [object Object].<anonymous> (/home/rof/src/github.com/DigitalInnovation/staffdirectory/test/e2e/app.js:112:34) Finished in 42.619 seconds 13 tests, 19 assertions, 2 failures Shutting down selenium standalone server. [launcher] 0 instance(s) of WebDriver still running [launcher] chrome #1 failed 2 test(s) [launcher] overall: 2 failed spec(s) [launcher] Process exited with error code 1 >> Fatal error: protractor exited with code: 1
这是我的带有测试的 app.js 文件:
var env = require('./mockservice.js');
describe("Application", function() {
var ptor;
beforeEach(function() {
ptor = browser;
ptor.addMockModule('MockServicesModule', env.httpBackendMock);
ptor.ignoreSynchronization = false;
browser.get('/');
ptor.ignoreSynchronization = true;
ptor.sleep(1000);
});
it('should verify the title', function() {
ptor.getTitle().then(function(title) {
expect(title).toBe('Store Directory');
});
});
describe("Search Bar", function() {
it('should enable input of data and return search result in list instantly', function() {
var searchBar = element.all(by.css('.store_text'));
searchBar.click();
searchBar.sendKeys("argyle");
var name = element.all(by.css('.name'));
expect(name.get(0).getText()).toEqual('Argyle St. Glasgow');
var number = element.all(by.css('.number'));
expect(number.get(0).getText()).toEqual('657');
});
it('should allow deletion of data that should instantly return all stores', function() {
var searchBar = element.all(by.css('.store_text'));
searchBar.click();
searchBar.sendKeys("argyle");
ptor.sleep(1000);
searchBar.sendKeys(protractor.Key.BACK_SPACE);
searchBar.sendKeys(protractor.Key.BACK_SPACE);
searchBar.sendKeys(protractor.Key.BACK_SPACE);
searchBar.sendKeys(protractor.Key.BACK_SPACE);
searchBar.sendKeys(protractor.Key.BACK_SPACE);
searchBar.sendKeys(protractor.Key.BACK_SPACE);
var name = element.all(by.css('.name'));
expect(name.get(0).getText()).toEqual('Aberdeen');
var number = element.all(by.css('.number'));
expect(number.get(0).getText()).toEqual('2587');
});
});
describe("Filter", function() {
it('should sort stores by name a-z', function() {
var filterBtn = element.all(by.css('.sort_text'));
filterBtn.click();
var azButton = element.all(by.cssContainingText('option', 'Stores A-Z'));
azButton.click();
ptor.sleep(1000);
var nameaz = element.all(by.css('.name'));
expect(nameaz.get(0).getText()).toEqual('Aberdeen');
var numberaz = element.all(by.css('.number'));
expect(numberaz.get(0).getText()).toEqual('2587');
});
it('should sort stores by name z-a', function() {
var filterBtn = element.all(by.css('.sort_text'));
filterBtn.click();
var zaButton = element.all(by.cssContainingText('option', 'Stores Z-A'));
zaButton.click();
ptor.sleep(1000);
var nameza = element.all(by.css('.name'));
expect(nameza.get(0).getText()).toEqual('Ayr');
var numberza = element.all(by.css('.number'));
expect(numberza.get(0).getText()).toEqual('2024');
});
it('should sort stores by number 0-9', function() {
var filterBtn = element.all(by.css('.sort_text'));
filterBtn.click();
var noButton = element.all(by.cssContainingText('option', 'Store Number 0-9'));
noButton.click();
ptor.sleep(1000);
var name09 = element.all(by.css('.name'));
expect(name09.get(0).getText()).toEqual('Anniesland');
var number09 = element.all(by.css('.number'));
expect(number09.get(0).getText()).toEqual('397');
});
it('should sort stores by number 9-0', function() {
var filterBtn = element.all(by.css('.sort_text'));
filterBtn.click();
var noButton = element.all(by.cssContainingText('option', 'Store Number 9-0'));
noButton.click();
ptor.sleep(1000);
var name90 = element.all(by.css('.name'));
expect(name90.get(0).getText()).toEqual('Aberdeen');
var number90 = element.all(by.css('.number'));
expect(number90.get(0).getText()).toEqual('2587');
});
});
describe("Store Name And Number", function() {
it('should show 4 store names', function() {
var names = element.all(by.repeater('store in stores'));
ptor.sleep(1000);
expect(names.count()).toBe(4);
});
it('should show 4 store numbers', function() {
var number04 = element.all(by.repeater('store in stores'));
ptor.sleep(1000);
expect(number04.count()).toBe(4);
});
it('should show details of the store roles and numbers when either the store name or store number is clicked on', function () {
var label = element.all(by.id('label')).first();
label.click();
ptor.sleep(1000);
var roles = element.all(by.repeater('contact in storeInfo.contacts'));
expect(roles.count()).toBe(2);
});
it('should close back to the main list after expanding more store details', function () {
var label = element.all(by.id('label')).first();
label.click();
ptor.sleep(1000);
label.click();
ptor.sleep(1000);
var stores = element.all(by.repeater('store in stores'));
expect(stores.count()).toBe(4);
});
});
describe("Store search", function() {
it('should show search results instantly', function() {
var label = element.all(by.id('label')).first();
label.click();
ptor.sleep(1000);
var storeSearch = element.all(by.css('.role_text'));
storeSearch.click();
ptor.sleep(1000);
storeSearch.sendKeys("service");
ptor.sleep(1000);
var roles = element.all(by.repeater('contact in storeInfo.contacts'));
expect(roles.count()).toBe(1);
});
it('should allow deletion of data to show an unfiltered list of roles', function() {
var label = element.all(by.id('label')).get(1);
label.click();
ptor.sleep(1000);
var storeSearch = element.all(by.css('.role_text'));
storeSearch.click();
ptor.sleep(1000);
storeSearch.sendKeys("cold");
ptor.sleep(1000);
var roles = element.all(by.repeater('contact in storeInfo.contacts'));
expect(roles.count()).toBe(1);
storeSearch.sendKeys(protractor.Key.BACK_SPACE);
storeSearch.sendKeys(protractor.Key.BACK_SPACE);
storeSearch.sendKeys(protractor.Key.BACK_SPACE);
storeSearch.sendKeys(protractor.Key.BACK_SPACE);
ptor.sleep(1000);
expect(roles.count()).toBe(2);
});
});
});
任何帮助将非常感激。我认为可以通过更多睡眠来修复“索引超出范围”错误,但对另一个错误一无所知……我们正在使用模拟数据进行这些测试。