Транспортир: случайный сбой теста

Итак, я только начал работать над тестами транспортира и столкнулся со следующей проблемой - мои тесты непоследовательно терпят неудачу. Иногда тест может пройти, а в следующий раз - нет. Причины сбоя очень разные, это может быть из-за того, что не удалось найти элемент на странице или в элементе нет текста (даже если он есть).

Я работаю на 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: $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');
    });
  });
});

А вот некоторые функции драйвера, на которые я ссылаюсь в тесте выше

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

Я не могу загрузить сюда весь код, чтобы вы лучше понимали, потому что у меня много кода до этого момента, но приведенный выше код является точным примером подхода, который я использую везде, видит ли кто-нибудь проблему в моем коде ? Спасибо.

у тебя есть аниматоны? если да - попробуйте тесты со всеми отключенными

Petr Averyanov 16.12.2018 01:35

Я отключил все анимации, спасибо за предложение

Lunin Roman 16.12.2018 07:17

Произошел ли сбой на том же it? Одна из возможных причин, я думаю, это то, что вы пропустили await в какой-то строке кода. Вы можете запускать один тестовый файл много раз, если он всегда может пройти, затем запускать другой тестовый файл, пока не найдете нестабильный тестовый файл, затем запускать его блок в этом нестабильном файле, чтобы сузить нестабильный блок, используйте тот же способ сузьте, какая строка кода нестабильна, тогда вы получите, какая строка кода пропущена, подождите

yong 17.12.2018 15:30

очень хороший момент! хотя это была моя первая мысль, я перепроверил код и не нашел отсутствующего await. и он не работает, скорее всего, на разных блоках, непоследовательно, поэтому я не могу найти причину, сводящую меня с ума

Lunin Roman 17.12.2018 15:35
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
4
1 972
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Похоже, это могло быть связано с асинхронным javascript.

browser.ignoreSynchronization = true; оказывает глобальное влияние на все ваши тесты. возможно, вам придется вернуть его в значение false, поэтому транспортир ждет, пока angular завершит рендеринг страницы. например в или до вашей второй функции beforeEach

Я использую последнюю версию транспортира, где ignoreSynchronization давно устарел.

Lunin Roman 17.12.2018 10:29

Прежде всего, добавьте этот блок перед экспортом конфигурации

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

это, по сути, красочно выводит на консоль, если вы где-то пропустили async / await, и это дает уверенность, что вы ничего не пропустили.

Во-вторых, я бы установил плагин «protractor-console», чтобы убедиться, что в консоли браузера нет ошибок / отклонений (т.е. исключить возможность проблем со стороны вашего приложения), и добавить в вашу конфигурацию

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

Тогда следующая проблема, которую я ожидал бы с этими знаками, - это неправильные функции ожидания. В идеале вы должны тестировать их отдельно при разработке проекта e2e, но поскольку все уже написано, я расскажу вам, как я их отлаживал. Обратите внимание, этот подход, вероятно, не поможет вам, если ваши действия менее секунды (т.е. вы их не заметите). В противном случае следуйте этой цепочке.

1) Я создал конфигурацию запуска в WebStorm, как описано в моем комментарии здесь (найдите мой) Как отлаживать тесты углового транспортира в WebStorm

2) Установите точку останова в первой строке теста, который я хочу отлаживать.

3) Затем выполните свой тест построчно, используя созданную конфигурацию запуска.

Когда вы начинаете процесс отладки, webstorm открывает панель с тремя разделами: фреймы, консоль, переменные. Если в разделе переменных есть сообщение connected to localhost и никакие переменные не указаны, это означает, что ваш шаг все еще выполняется. После завершения загрузки вы можете увидеть все свои переменные и выполнить следующую команду. Итак, главный принцип здесь - вы нажимаете кнопку «Перешагнуть» и смотрите раздел переменных. ЕСЛИ ПЕРЕМЕННЫЕ ПОЯВЛЯЮТСЯ ДО ЗАВЕРШЕНИЯ ЗАГРУЗКИ ПРИЛОЖЕНИЙ (метод ожидания выполнен, но приложение все еще загружается, что неверно), то вам нужно поработать над этим методом. Поступая таким образом, я обнаружил множество пробелов в моих пользовательских методах ожидания.

И, наконец, если это не сработает, прикрепите трассировку стека ваших ошибок и пингуйте меня.

Конечно, если это помогло вам понять, в чем заключалась ваша проблема, примите ответ как правильный. В противном случае выясните, что вас беспокоит. Спасибо

Sergey Pleshakov 18.12.2018 19:19

Меня беспокоит этот фрагмент кода

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 при настройке пакета ниже него. Вы не разместили код для InsurerRefundDriver.getTitle(), но если этот код не отправляет браузер по правильному URL-адресу, а затем ожидает завершения загрузки страницы, это проблема. Вероятно, у вас должен быть await InsurerViewDriver.goToPage(); в пункте beforeEach.

Ответ принят как подходящий

После некоторого времени исследования я обнаружил, в чем проблема. Причина заключалась в том, как я перемещаюсь по приложению.

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

Оказывается, этот метод browser.get разрешается при изменении URL-адреса, но теперь, когда 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');
  };

Вы должны убедиться, что страница выполнила всю работу по компиляции, прежде чем двигаться дальше.

Это правда, что browser.get только ждет загрузки страницы, обслуживаемой сервером, после чего запускается весь JavaScript, влияющий на DOM. Однако и element, и element.all ждут, пока Angular завершит обновление DOM, прежде чем искать элементы (см. Подробнее как это работает), поэтому в целом вам не нужно добавлять явный wait, подобный этому. С другой стороны, если вы используете AJAX или другой JavaScript за пределами Angular, то да, вам нужно явно дождаться завершения этого.

Old Pro 21.12.2018 07:32

Другие вопросы по теме