Модульное тестирование JS-приложения с перезагрузкой страницы и iframe

У меня есть приложение, для которого перезагрузка/перемещение страниц и iframe имеют решающее значение, и эти части кажутся очень сложными, чтобы покрыть модульные тесты.

Я хочу писать smt. например:

it('should fire appropriate callbacks on start and page reload', function() {
  app.start();
  expect(app.onStart).toHaveBeenCalled();
  page.reload();
  expect(app.onRestart).toHaveBeenCalled();
}

it('should know whether it runs in iframe or not', function() {
  expect(app.isInIframe()).toBe(false);
  iframe = createTestIframe();
  expect(iframe.getApp().isInIframe()).toBe(true);
}

Структуры модульного тестирования, которые я знаю (mocha, Jasmine, QUnit), предназначены для выполнения всего набора тестов на одной странице в верхнем контексте.

С другой стороны, рамки функционального тестирования (FuncUnit, TestCafé, Selenium WebDriver), похоже, сосредоточены на абстракциях высокого уровня, таких как "щелчок элемента", "проверка значения элемента" и т.д., не дающий возможности вникать в выполнение кода.

Отказ от ответственности: я довольно новичок в тестировании в целом, поэтому, возможно, мне стоит рассмотреть проблему с другой точки зрения вообще.

Ответ 1

Intern предназначен именно для того, чтобы включить эти виды функциональных тестов в подобных ситуациях и был фактически создан из-за проблемы, которую вы описать, где существующие рамки тестирования JS не включали эти виды взаимодействий. Он включает в себя интерфейс функционального тестирования, который будет работать так, предполагая, что app находится на стороне Node.js, вы делаете что-то вроде этого:

define([ 'intern!bdd', 'intern/chai!expect', 'my/app' ], function (bdd, expect, app) {
   var it = bdd.it;

   it('should fire appropriate callbacks on start and page reload', function() {
     app.start();
     return this.remote.get('http://path/to/server')
       .then(function () {
         expect(app.onStart).toHaveBeenCalled();
       })
       .refresh()
       .then(function () {
         expect(app.onRestart).toHaveBeenCalled();
       });
   });

   // ...etc.
});

Учебник для начинающих дает лучший обзор различий между модульным и функциональным тестированием и как использовать оба. В отличие от какого-либо другого предложения, такого как CasperJS, он фактически проведет ваши функциональные тесты против реальных браузеров, используя стандартный API WebDriver, в сочетании с сервисом, например Sauce Labs или вашим собственным сервером Selenium.

Ответ 2

Те тесты, которые вы описываете, кажутся полными интеграционными тестами, а не модульными тестами (refreshes/iframes).

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

Для типа тестов, которые вы хотите выполнить (в том числе iframes/refreshes), лучше использовать инструмент для тестирования интеграции, например Selenium IDE.

Этот инструмент имеет макрорекордер, который регистрирует действия вашего браузера в тесте и позволяет воспроизводить действия и добавлять утверждения для проверки результатов теста. Посмотрите на это демо-видео, чтобы увидеть, как просто использовать.

Этот тип интеграционных тестов дополняет, но не заменяет тип тестируемых модулей.

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

Ответ 3

Вы можете попробовать CasperJS. Он запускает функциональные тесты в PhantomJS, и вы можете оценить произвольный код на тестовых страницах. Для вашего случая вы должны сделать что-то вроде этого:

casper.test.begin('iframe', 1, function (test) {
  casper
    .start('your.page.url')
    .thenEvaluate(function () {
      window.iframe = createTestIframe()
    })
    .then(function () {
      test.assertEval(function () {
        return iframe.getApp().isInIframe()
      })
    })
})