量角器:随机测试失败
protractor: random test fail
所以我刚开始进行量角器测试,但遇到了以下问题 - 我的测试不一致地失败了。有时测试可能通过了,而下一次却失败了。失败的原因非常不同,可能是因为它未能在页面上找到元素或元素中没有文本(即使它有)。
我 运行 在 Ubuntu 14.04,同样的问题与 Chrome Version 71.0.3578.80
和 Firefox Version 60.0.2
相关。 AngularJS Version 1.7.2
和 Protractor Version 5.4.0
。我认为问题出在我的代码中,因此我在下面提供了一个现有代码库的示例。
这是我的量角器配置
exports.config = {
rootElement: '[ng-app="myapp"]',
framework: 'jasmine',
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['./e2e/**/*protractor.js'],
SELENIUM_PROMISE_MANAGER: false,
baseUrl: 'https://localhost/',
allScriptsTimeout: 20000,
jasmineNodeOpts: {
defaultTimeoutInterval: 100000,
},
capabilities: {
browserName: 'firefox',
marionette: true,
acceptInsecureCerts: true,
'moz:firefoxOptions': {
args: ['--headless'],
},
},
}
这里是 chrome 浏览器的功能
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: [ "--headless", "--disable-gpu", "--window-size=1920,1080" ]
}
},
最后,我失败了几次的测试套件
const InsurerViewDriver = require('./insurer-view.driver');
const InsurerRefundDriver = require('./insurer-refund.driver');
const { PageDriver } = require('@utils/page');
const { NotificationsDriver } = require('@utils/service');
const moment = require('moment');
describe(InsurerViewDriver.pageUrl, () => {
beforeAll(async () => {
await InsurerViewDriver.goToPage();
});
it('- should test "Delete" button', async () => {
await InsurerViewDriver.clickDelete();
await NotificationsDriver.toBeShown('success');
await PageDriver.userToBeNavigated('#/setup/insurers');
await InsurerViewDriver.goToPage();
});
describe('Should test Refunds section', () => {
it('- should test refund list content', async () => {
expect(await InsurerRefundDriver.getTitle()).toEqual('REFUNDS');
const refunds = InsurerRefundDriver.getRefunds();
expect(await refunds.count()).toBe(1);
const firstRow = refunds.get(0);
expect(await firstRow.element(by.binding('item.name')).getText()).toEqual('Direct');
expect(await firstRow.element(by.binding('item.amount')).getText()).toEqual('$ 50.00');
expect(await firstRow.element(by.binding('item.number')).getText()).toEqual('');
expect(await firstRow.element(by.binding('item.date')).getText()).toEqual(moment().format('MMMM DD YYYY'));
});
it('- should test add refund action', async () => {
await InsurerRefundDriver.openNewRefundForm();
const NewRefundFormDriver = InsurerRefundDriver.getNewRefundForm();
await NewRefundFormDriver.setPayment(`#555555, ${moment().format('MMMM DD YYYY')} (amount: ,000, rest: ,500)`);
await NewRefundFormDriver.setPaymentMethod('Credit Card');
expect(await NewRefundFormDriver.getAmount()).toEqual('0');
await NewRefundFormDriver.setAmount(200.05);
await NewRefundFormDriver.setAuthorization('qwerty');
await NewRefundFormDriver.submit();
await NotificationsDriver.toBeShown('success');
const interactions = InsurerRefundDriver.getRefunds();
expect(await interactions.count()).toBe(2);
expect(await InsurerViewDriver.getInsurerTitleValue('Balance:')).toEqual('Balance: $ 2,200.05');
expect(await InsurerViewDriver.getInsurerTitleValue('Wallet:')).toEqual('Wallet: $ 4,799.95');
});
});
});
这里是驱动程序的一些功能,我在上面的测试中引用了这些功能
// PageDriver.userToBeNavigated
this.userToBeNavigated = async function(url) {
return await browser.wait(
protractor.ExpectedConditions.urlContains(url),
5000,
`Expectation failed - user to be navigated to "${url}"`
);
};
this.pageUrl = '#/insurer/33';
// InsurerViewDriver.goToPage
this.goToPage = async () => {
await browser.get(this.pageUrl);
};
// InsurerViewDriver.clickDelete()
this.clickDelete = async () => {
await $('[ng-click="$ctrl.removeInsurer()"]').click();
await DialogDriver.toBeShown('Are you sure you want to remove this entry?');
await DialogDriver.confirm();
};
// NotificationsDriver.toBeShown
this.toBeShown = async (type, text) => {
const awaitSeconds = 6;
return await browser.wait(
protractor.ExpectedConditions.presenceOf(
text ? element(by.cssContainingText('.toast-message', text)) : $(`.toast-${type}`)
),
awaitSeconds * 1000,
`${type} notification should be shown within ${awaitSeconds} sec`
);
}
// InsurerRefundDriver.getRefunds()
this.getRefunds = () => $('list-refunds-component').all(by.repeater('item in $data'));
// InsurerViewDriver.getInsurerTitleValue
this.getInsurerTitleValue = async (text) => {
return await element(by.cssContainingText('header-content p', text)).getText();
};
我不能在这里上传整个代码来让你更好地理解,因为到目前为止我有很多代码,但上面提供的代码是我在任何地方都使用的方法的确切示例,有没有人看到我的代码有问题吗?谢谢。
这似乎是由于异步 javascript。
browser.ignoreSynchronization = 真;对您的所有测试都有全局影响。您可能必须将其设置回 false,因此量角器会等待 angular 完成页面呈现。例如在你的第二个 beforeEach 函数中或之前
首先在导出配置之前添加这个块
process.on("unhandledRejection", ({message}) => {
console.log("\x1b[36m%s\x1b[0m", `Unhandled rejection: ${message}`);
});
如果您错过任何地方 async/await,这基本上会以丰富多彩的方式记录到控制台,并且它会让您确信您没有错过任何事情。
其次,我会安装 "protractor-console" 插件,以确保浏览器控制台中没有 errors/rejections(即排除您的应用程序方面出现问题的可能性)并添加到您的配置中
plugins: [{
package: "protractor-console",
logLevels: [ "severe" ]
}]
那么我预计这些标志的下一个问题是不正确的等待功能。理想情况下,您必须在开发 e2e 项目时分别测试它们,但由于已经全部编写完毕,我将告诉您我是如何调试它们的。请注意,如果您的操作时间少于一秒(即您无法注意到它们),此方法可能对您没有帮助。否则遵循此链。
1) 我在 WebStorm 中创建了 运行 配置,如我在此处的评论中所述(找到我的)How to debug angular protractor tests in WebStorm
2) 在我要调试的测试的第一行设置断点
3) 然后使用创建的 运行 配置逐行执行测试。
当您开始调试过程时,webstorm 会打开一个包含三个部分的面板:框架、控制台、变量。当变量部分有一条消息 connected to localhost
并且没有列出任何变量时,这意味着您的步骤仍在执行。加载完成后,您可以看到所有变量,然后可以执行下一个命令。所以这里的主要原则是你点击 Step Over 按钮并观察变量部分。 IF VARIABLES APPEAR APPEAR BEFORE THE APPS LOADING COMPLETED(等待方法已执行,但应用程序仍在加载,这是错误的)那么你需要处理这个方法。通过这种方式,我在自定义等待方法中发现了很多差距。
最后,如果这不起作用,请附上错误的堆栈跟踪并 ping 我
我很担心这段代码
describe(InsurerViewDriver.pageUrl, () => {
beforeAll(async () => {
await InsurerViewDriver.goToPage();
});
it('- should test "Delete" button', async () => {
await InsurerViewDriver.clickDelete();
await NotificationsDriver.toBeShown('success');
await PageDriver.userToBeNavigated('#/setup/insurers');
await InsurerViewDriver.goToPage(); // WHY IS THIS HERE?
});
describe('Should test Refunds section', () => {
it('- should test refund list content', async () => {
// DOESN'T THIS NEED SOME SETUP?
expect(await InsurerRefundDriver.getTitle()).toEqual('REFUNDS');
// <truncated>
您不应依赖第一个 it
子句来设置它下面的套件。您没有 post InsurerRefundDriver.getTitle()
的代码,但如果该代码没有将浏览器发送到正确的 URL 然后等待页面完成加载,这是一个问题。您可能应该在 beforeEach
子句中包含 await InsurerViewDriver.goToPage();
。
经过一段时间的研究,我发现了问题所在。原因是我浏览应用程序的方式。
this.goToPage = async () => {
await browser.get(this.pageUrl);
};
事实证明,当 url 更改时 browser.get
方法正在解析,但现在当 angularjs 完成编译时。我在每个测试套件中都使用了相同的方法,这就是为什么我的测试不一致地失败,有时页面在测试开始前没有完全加载。
所以这是一个成功的方法
this.goToPage = async () => {
await browser.get(this.pageUrl);
await browser.wait(EC.presenceOf(`some important element`), 5000, 'Element did not appear after route change');
};
您应该确保页面在继续之前完成所有编译工作。
所以我刚开始进行量角器测试,但遇到了以下问题 - 我的测试不一致地失败了。有时测试可能通过了,而下一次却失败了。失败的原因非常不同,可能是因为它未能在页面上找到元素或元素中没有文本(即使它有)。
我 运行 在 Ubuntu 14.04,同样的问题与 Chrome Version 71.0.3578.80
和 Firefox Version 60.0.2
相关。 AngularJS Version 1.7.2
和 Protractor Version 5.4.0
。我认为问题出在我的代码中,因此我在下面提供了一个现有代码库的示例。
这是我的量角器配置
exports.config = {
rootElement: '[ng-app="myapp"]',
framework: 'jasmine',
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['./e2e/**/*protractor.js'],
SELENIUM_PROMISE_MANAGER: false,
baseUrl: 'https://localhost/',
allScriptsTimeout: 20000,
jasmineNodeOpts: {
defaultTimeoutInterval: 100000,
},
capabilities: {
browserName: 'firefox',
marionette: true,
acceptInsecureCerts: true,
'moz:firefoxOptions': {
args: ['--headless'],
},
},
}
这里是 chrome 浏览器的功能
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: [ "--headless", "--disable-gpu", "--window-size=1920,1080" ]
}
},
最后,我失败了几次的测试套件
const InsurerViewDriver = require('./insurer-view.driver');
const InsurerRefundDriver = require('./insurer-refund.driver');
const { PageDriver } = require('@utils/page');
const { NotificationsDriver } = require('@utils/service');
const moment = require('moment');
describe(InsurerViewDriver.pageUrl, () => {
beforeAll(async () => {
await InsurerViewDriver.goToPage();
});
it('- should test "Delete" button', async () => {
await InsurerViewDriver.clickDelete();
await NotificationsDriver.toBeShown('success');
await PageDriver.userToBeNavigated('#/setup/insurers');
await InsurerViewDriver.goToPage();
});
describe('Should test Refunds section', () => {
it('- should test refund list content', async () => {
expect(await InsurerRefundDriver.getTitle()).toEqual('REFUNDS');
const refunds = InsurerRefundDriver.getRefunds();
expect(await refunds.count()).toBe(1);
const firstRow = refunds.get(0);
expect(await firstRow.element(by.binding('item.name')).getText()).toEqual('Direct');
expect(await firstRow.element(by.binding('item.amount')).getText()).toEqual('$ 50.00');
expect(await firstRow.element(by.binding('item.number')).getText()).toEqual('');
expect(await firstRow.element(by.binding('item.date')).getText()).toEqual(moment().format('MMMM DD YYYY'));
});
it('- should test add refund action', async () => {
await InsurerRefundDriver.openNewRefundForm();
const NewRefundFormDriver = InsurerRefundDriver.getNewRefundForm();
await NewRefundFormDriver.setPayment(`#555555, ${moment().format('MMMM DD YYYY')} (amount: ,000, rest: ,500)`);
await NewRefundFormDriver.setPaymentMethod('Credit Card');
expect(await NewRefundFormDriver.getAmount()).toEqual('0');
await NewRefundFormDriver.setAmount(200.05);
await NewRefundFormDriver.setAuthorization('qwerty');
await NewRefundFormDriver.submit();
await NotificationsDriver.toBeShown('success');
const interactions = InsurerRefundDriver.getRefunds();
expect(await interactions.count()).toBe(2);
expect(await InsurerViewDriver.getInsurerTitleValue('Balance:')).toEqual('Balance: $ 2,200.05');
expect(await InsurerViewDriver.getInsurerTitleValue('Wallet:')).toEqual('Wallet: $ 4,799.95');
});
});
});
这里是驱动程序的一些功能,我在上面的测试中引用了这些功能
// PageDriver.userToBeNavigated
this.userToBeNavigated = async function(url) {
return await browser.wait(
protractor.ExpectedConditions.urlContains(url),
5000,
`Expectation failed - user to be navigated to "${url}"`
);
};
this.pageUrl = '#/insurer/33';
// InsurerViewDriver.goToPage
this.goToPage = async () => {
await browser.get(this.pageUrl);
};
// InsurerViewDriver.clickDelete()
this.clickDelete = async () => {
await $('[ng-click="$ctrl.removeInsurer()"]').click();
await DialogDriver.toBeShown('Are you sure you want to remove this entry?');
await DialogDriver.confirm();
};
// NotificationsDriver.toBeShown
this.toBeShown = async (type, text) => {
const awaitSeconds = 6;
return await browser.wait(
protractor.ExpectedConditions.presenceOf(
text ? element(by.cssContainingText('.toast-message', text)) : $(`.toast-${type}`)
),
awaitSeconds * 1000,
`${type} notification should be shown within ${awaitSeconds} sec`
);
}
// InsurerRefundDriver.getRefunds()
this.getRefunds = () => $('list-refunds-component').all(by.repeater('item in $data'));
// InsurerViewDriver.getInsurerTitleValue
this.getInsurerTitleValue = async (text) => {
return await element(by.cssContainingText('header-content p', text)).getText();
};
我不能在这里上传整个代码来让你更好地理解,因为到目前为止我有很多代码,但上面提供的代码是我在任何地方都使用的方法的确切示例,有没有人看到我的代码有问题吗?谢谢。
这似乎是由于异步 javascript。
browser.ignoreSynchronization = 真;对您的所有测试都有全局影响。您可能必须将其设置回 false,因此量角器会等待 angular 完成页面呈现。例如在你的第二个 beforeEach 函数中或之前
首先在导出配置之前添加这个块
process.on("unhandledRejection", ({message}) => {
console.log("\x1b[36m%s\x1b[0m", `Unhandled rejection: ${message}`);
});
如果您错过任何地方 async/await,这基本上会以丰富多彩的方式记录到控制台,并且它会让您确信您没有错过任何事情。
其次,我会安装 "protractor-console" 插件,以确保浏览器控制台中没有 errors/rejections(即排除您的应用程序方面出现问题的可能性)并添加到您的配置中
plugins: [{
package: "protractor-console",
logLevels: [ "severe" ]
}]
那么我预计这些标志的下一个问题是不正确的等待功能。理想情况下,您必须在开发 e2e 项目时分别测试它们,但由于已经全部编写完毕,我将告诉您我是如何调试它们的。请注意,如果您的操作时间少于一秒(即您无法注意到它们),此方法可能对您没有帮助。否则遵循此链。
1) 我在 WebStorm 中创建了 运行 配置,如我在此处的评论中所述(找到我的)How to debug angular protractor tests in WebStorm
2) 在我要调试的测试的第一行设置断点
3) 然后使用创建的 运行 配置逐行执行测试。
当您开始调试过程时,webstorm 会打开一个包含三个部分的面板:框架、控制台、变量。当变量部分有一条消息 connected to localhost
并且没有列出任何变量时,这意味着您的步骤仍在执行。加载完成后,您可以看到所有变量,然后可以执行下一个命令。所以这里的主要原则是你点击 Step Over 按钮并观察变量部分。 IF VARIABLES APPEAR APPEAR BEFORE THE APPS LOADING COMPLETED(等待方法已执行,但应用程序仍在加载,这是错误的)那么你需要处理这个方法。通过这种方式,我在自定义等待方法中发现了很多差距。
最后,如果这不起作用,请附上错误的堆栈跟踪并 ping 我
我很担心这段代码
describe(InsurerViewDriver.pageUrl, () => {
beforeAll(async () => {
await InsurerViewDriver.goToPage();
});
it('- should test "Delete" button', async () => {
await InsurerViewDriver.clickDelete();
await NotificationsDriver.toBeShown('success');
await PageDriver.userToBeNavigated('#/setup/insurers');
await InsurerViewDriver.goToPage(); // WHY IS THIS HERE?
});
describe('Should test Refunds section', () => {
it('- should test refund list content', async () => {
// DOESN'T THIS NEED SOME SETUP?
expect(await InsurerRefundDriver.getTitle()).toEqual('REFUNDS');
// <truncated>
您不应依赖第一个 it
子句来设置它下面的套件。您没有 post InsurerRefundDriver.getTitle()
的代码,但如果该代码没有将浏览器发送到正确的 URL 然后等待页面完成加载,这是一个问题。您可能应该在 beforeEach
子句中包含 await InsurerViewDriver.goToPage();
。
经过一段时间的研究,我发现了问题所在。原因是我浏览应用程序的方式。
this.goToPage = async () => {
await browser.get(this.pageUrl);
};
事实证明,当 url 更改时 browser.get
方法正在解析,但现在当 angularjs 完成编译时。我在每个测试套件中都使用了相同的方法,这就是为什么我的测试不一致地失败,有时页面在测试开始前没有完全加载。
所以这是一个成功的方法
this.goToPage = async () => {
await browser.get(this.pageUrl);
await browser.wait(EC.presenceOf(`some important element`), 5000, 'Element did not appear after route change');
};
您应该确保页面在继续之前完成所有编译工作。