Настройка тестовых данных для тестирования полного стека одностраничного веб-приложения и его серверной части

Краткая версия моего вопроса:

В тестах Cucumber, написанных для одностраничного веб-приложения Angular, как мне выполнить задачи, обычно выполняемые в «заданном» разделе сценария (например, настройка тестовых данных, определение ассоциаций записей базы данных и обеспечение чистого состояния базы данных между тестами ) при тестировании полного стека, как интерфейсного приложения, так и его серверной части? Исходный код приложения хранится в двух отдельных репозиториях Git: один для интерфейсного приложения, а другой - для внутреннего. Серверная часть написана на Ruby с использованием гема Rails API. Проблема тестирования полного стека этого приложения связана с тем, что это фактически два приложения, в отличие от более традиционного приложения Ruby on Rails, не реализованного как одностраничное приложение.

Полная версия моего вопроса:

Я хочу написать серию тестов Cucumber для веб-приложения. Приложение состоит из внешнего одностраничного приложения, написанного на Angular, и внутреннего API, написанного с использованием Rails API. Исходный код для внешнего интерфейса и исходный код для внутреннего интерфейса находятся в своих собственных репозиториях Git, обеспечивая четкое разделение между двумя базами кода. Кроме того, приложение использует MySQL и Elasticsearch.

Раньше я использовал Cucumber в предыдущих проектах Ruby on Rails. Эти проекты не разрабатывались как одностраничные приложения. В этих проектах было легко создавать объекты Ruby в качестве тестовых данных в определениях шагов Cucumber. Например, рассмотрим следующий файл функций из проекта Rails, который не был одностраничным приложением:

# features/home_page.feature
Feature: Home page

  Scenario: Viewing application's home page
    Given there's a post titled "My first" with "Hello, BDD world!" content
    When I am on the homepage
    Then I should see the "My first" post

Шаги в этом файле функций могут быть реализованы с помощью следующих определений шагов:

# features/step_definitions/home_page_steps.rb
Given(/^there's a post titled "(.*?)" with "(.*?)" content$/) do |title, content|
  @post = FactoryGirl.create(:post, title: title, content: content)
end

When(/^I am on the homepage$/) do
  visit root_path
end

Then(/^I should see the "(.*?)" post$/) do |title|
  @post = Post.find_by_title(title)

  page.should have_content(@post.title)summary of the question
  page.should have_content(@post.content)
end

В проектах Ruby on Rails, которые не были разработаны как одностраничные приложения, инструменты тестирования могут быть включены в проект как драгоценные камни Ruby. Для меня эти инструменты включают:

group :test do
  gem 'shoulda-matchers'
  gem 'cucumber-rails', require: false
  gem 'database_cleaner'
  gem 'selenium-webdriver'
end

group :development, :test do
  gem 'factory_girl_rails'
end

Как видите, это включает Factory Girl, используемую для настройки объектов Ruby в качестве тестовых данных и определения ассоциаций записей базы данных, и Database Cleaner, используемую для обеспечения чистого состояния базы данных между тестами. Включение Selenium WebDriver требуется для сценариев Cucumber, использующих JavaScript.

В случае моего одностраничного приложения ситуация иная. Как описано выше, приложение разбито на две отдельные базы кода: одну для внешнего одностраничного приложения Angular, а другую - для внутреннего интерфейса Rails API.

Однако одностраничные приложения, такие как мой проект, по-прежнему имеют те же требования к тестированию, что и более традиционные приложения Rails, не созданные как одностраничные приложения. Необходимо протестировать полный стек приложения, чтобы убедиться, что каждый компонент, как внешний, так и внутренний, работают вместе, как ожидалось. Необходимо будет определить шаги Cucumber, которые создают «заданные» предварительные условия перед тестом, и необходимо будет обеспечить чистую базу данных между тестами.

Как мне протестировать с помощью Cucumber такое приложение, как это, с двумя базами кода, реализованное как одностраничное приложение? Для JavaScript доступна версия Cucumber под названием CucumberJS. Однако я не знаю, как создавать фикстуры, ассоциации записей и обеспечивать чистую базу данных между тестами с помощью CucumberJS. Также существует инструмент для тестирования JavaScript, написанный на Angular, под названием Protractor. Я предполагаю, что этот инструмент заменит Selenium WebDriver.


person Aristarkh Artemiy    schedule 14.06.2015    source источник


Ответы (1)


Ответьте на вопрос краткой версии.

Сложность тестирования полного стека этого приложения связана с тем, что на самом деле это два приложения.

То, что вы описываете, типично для одностраничных приложений. У меня есть несколько примеров спа (покрытых E2E), где бэкэнд представляет собой RESTful API в PHP, Dot.net или когда-либо облачный веб-сервис (parse.com). С моей точки зрения, лучше всего рассматривать такие приложения, повторяя их снова, как два разных приложения и писать по два разных набора тестов для каждого. Я считаю, что это единственный способ писать стабильные, масштабируемые и быстрые тесты e2e.

По сути, мы создаем локальный непостоянный mock api (в javascript) специально для тестирования. Вот список требований / рекомендаций для такого API:

  1. Предоставляет фиктивные данные для испытательного костюма (читайте - он соединен с испытательным костюмом)
  2. Охватывает ровно столько методов, сколько нужно для прохождения тестов.
  3. Это непостоянно (параметры запроса - это все, что нужно для ответа, хотя из этого могут быть исключения). Все постоянство находится на клиенте (локальное хранилище, файлы cookie). Это важно, потому что просто очистите клиентское хранилище и обновите страницу - вы получите свежее состояние вашего приложения! Если вы тестируете монолит - вам придется очистить и повторно заполнить базу данных, что является пустой тратой времени и полностью немасштабируемым.
  4. Встроенный в репозиторий angular, он является частью набора для тестирования и процесса разработки.

Как это выглядит на практике. Допустим, мы тестируем функцию входа на веб-сайт, и у нее есть 3 сценария:

var homepage = require('pages/homepage'); //page object for home page

describe('Authentication', function () {

  beforeEach(function () {
    browser.manage().deleteAllCookies(); //clear persistance
    browser.refresh(); //refresh page
    homepage.get(); //go to homepage
  });

  it('should show error when user enters wrong credentials', function () {
    homepage.signIn('admin', '123'); //submit invalid credentials 
    expect(browser.getCurrentUrl()).toMatch('/home$');    
    expect(homepage.getSignInForm()).toContain('wrong credentials');
  });

});

Чтобы удовлетворить эти тесты, все, что нам нужно сделать, это реализовать api для запроса POST / входа в систему:

router.get('/login', function (req, res) {
  var username = req.body.username, password = req.body.password;

  if (password !== 'admin') {
    res.status(404).json({"error": "wrong credentials"});
  } else {
    res.json(User(username}));
  }
});
person Dziamid    schedule 15.06.2015