简体   繁体   English

量角器:随机测试失败

[英]protractor: random test fail

So I just started work on protractor tests and I'm facing the following problem - my tests fail inconsistently. 因此,我刚刚开始量角器测试,并且面临以下问题-我的测试始终失败。 Sometimes the test may pass and the next time it fails. 有时,测试可能会通过,而下一次失败。 Reasons to fail is very different, it may because it failed to find an element on a page or element does not have text in it (even if it has). 失败的原因非常不同,这可能是因为它未能在页面上找到元素或元素中没有文本(即使有)。

I'm running on Ubuntu 14.04, the same problem relevant for Chrome Version 71.0.3578.80 and Firefox Version 60.0.2 . 我在Ubuntu 14.04上运行,与Chrome Version 71.0.3578.80Firefox Version 60.0.2相关的相同问题。 AngularJS Version 1.7.2 and Protractor Version 5.4.0 . AngularJS Version 1.7.2Protractor Version 5.4.0 I believe the problem is somewhere in my code, so here below I provided an example of an existing code base. 我相信问题出在我的代码中,因此下面我提供了一个现有代码库的示例。

Here is my protractor config 这是我的量角器配置

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'],
     },
   },
}

And here capabilities for chrome browser chrome浏览器的功能

  capabilities: {
    browserName: 'chrome',
     chromeOptions: {
       args: [ "--headless", "--disable-gpu", "--window-size=1920,1080" ]
     }
  },

And finally, my test kit that failed a few times 最后,我的测试套件几次失败

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: $2,000, rest: $1,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');
    });
  });
});

And here some functions from driver's, that I'm referencing in the test above 这是驱动程序的一些功能,我在上面的测试中引用了这些功能

  // 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();
  };

I can't upload the whole code here to give you better understanding because I have a lot of code till this moment, but the code provided above is the exact sample of approach I'm using everywhere, does anyone see a problem in my code? 我无法在此处上载整个代码,以使您更好地理解,因为到目前为止,我还有很多代码,但是上面提供的代码是我在各处使用的方法的准确示例,有人在我的代码中看到问题吗? ? Thanks. 谢谢。

First of all add this block before exporting your config 首先,在导出配置之前添加此块

process.on("unhandledRejection", ({message}) => {
    console.log("\x1b[36m%s\x1b[0m", `Unhandled rejection: ${message}`);
});

this essentially colorfully logs to the console if you missed async/await anywhere, and it'll give confidence that you didn't miss anything. 如果您在任何地方错过了异步/等待,那么这基本上会以彩色方式记录到控制台,这将使您确信自己没有错过任何事情。

Second, I would install "protractor-console" plugin, to make sure there is no errors/rejections in the browser console (ie exclude possibility of issues from your app side) and add to your config 其次,我将安装“量角器控制台”插件,以确保浏览器控制台中没有错误/拒绝(即从应用程序侧排除问题的可能性)并添加到您的配置中

plugins: [{
    package: "protractor-console",
    logLevels: [ "severe" ]
}]

Then the next problem that I would expect with these signs is incorrect waiting functions. 然后,我期望这些标志出现的下一个问题是等待功能不正确。 Ideally you have to test them separately as you develop your e2e project, but since it's all written already I'll tell you how I debugged them. 理想情况下,在开发e2e项目时必须分别测试它们,但是由于已经全部编写完毕,因此我将告诉您如何调试它们。 Note, this approach won't probably help you if your actions are less than a sec (ie you can't notice them). 请注意,如果您的动作少于一秒(即您不会注意到它们),则此方法可能对您没有帮助。 Otherwise follow this chain. 否则,请遵循此链。

1) I created run configuration in WebStorm, as described in my comment here (find mine) How to debug angular protractor tests in WebStorm 1)我在WebStorm中创建了运行配置,如我的评论中所述(查找我的), 如何在WebStorm中调试角度量角器测试

2) Set a breakpoint in the first line of the test I want to debug 2)在要调试的测试的第一行中设置一个断点

3) Then execute your test line by line, using the created run config. 3)然后,使用创建的运行配置逐行执行测试。

When you start debugging process, webstorm opens up a panel with three sections: frames, console, variables. 当您开始调试过程时,webstorm将打开一个包含三个部分的面板:框架,控制台,变量。 When the variables section has a message connected to localhost and no variables listed, this means your step is still being executed. 当变量部分有一条connected to localhost的消息且未列出任何变量时,这意味着您的步骤仍在执行。 Once loading completed you can see all your variables and you can execute next command. 加载完成后,您可以看到所有变量,并且可以执行下一个命令。 So the main principle here is you click Step Over button and watch for variables section. 因此,这里的主要原理是单击“跳过”按钮,并注意变量部分。 IF VARIABLES APPEAR BEFORE THE APPS LOADING COMPLETED (the waiting method executed, but the app is still loading, which is wrong) then you need to work on this method. 如果变量在加载应用程序之前出现(已执行等待方法,但应用程序仍在加载,这是错误的),那么您需要使用此方法。 By going this way I identified a lot of gaps in my custom waiting methods. 通过这种方式,我在自定义等待方法中发现了很多空白。

And finally if this doesn't work, please attach stack trace of your errors and ping me 最后,如果这不起作用,请附加错误的堆栈跟踪信息并ping我

I'm concerned about this code snippet 我担心此代码段

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>

You should not depend on the first it clause to set up the suite below it. 您不应依赖第一个it子句在其下面设置套件。 You didn't post the code for InsurerRefundDriver.getTitle() but if that code does not send the browser to the correct URL and then wait for the page to finish loading, it is a problem. 您没有发布InsurerRefundDriver.getTitle()的代码,但是如果该代码未将浏览器发送到正确的URL,然后等待页面完成加载,则存在问题。 You should probably have await InsurerViewDriver.goToPage(); 您可能应该已经await InsurerViewDriver.goToPage(); in a beforeEach clause. beforeEach子句中。

It seems this could be due to asynchronous javascript. 看来这可能是由于异步javascript。

browser.ignoreSynchronization = true; browser.ignoreSynchronization = true; has a global effect for all your tests. 对您的所有测试都有全局影响。 you may have to set it back to false, so protractor waits for angular to be finished rendering the page. 您可能必须将其设置回false,因此量角器等待angular完成页面渲染。 eg in or before your second beforeEach function 例如在第二个beforeEach函数中或之前

After some time research I found what was the problem. 经过一段时间的研究,我发现了问题所在。 The cause was the way I'm navigate through the app. 原因是我浏览应用程序的方式。

  this.goToPage = async () => {
    await browser.get(this.pageUrl);
  };

Turns out, that browser.get method is being resolved when url changed, but now when angularjs done compile. 原来,当url更改时,但当angularjs完成编译时,将解决该browser.get方法。 I used the same approach in every test kit, that's why my tests were failing inconsistently, sometimes page was not fully loaded before test start. 我在每个测试工具包中都使用了相同的方法,这就是为什么我的测试始终失败的原因,有时在测试开始之前页面没有完全加载。

So here is an approach that did the trick 所以这是一种有效的方法

  this.goToPage = async () => {
    await browser.get(this.pageUrl);
    await browser.wait(EC.presenceOf(`some important element`), 5000, 'Element did not appear after route change');
  };

You should ensure that page done all the compiling job before moving on. 您应确保页面在继续操作之前完成了所有编译工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM