четверг, 17 июля 2014 г.

Ember тестирование

Когда я начал играться с Ember.js почти год назад, тестирируемость оставляла желать лучшего . Вы могли использовать модульное тестирование объекта без каких-либо проблем , но модульный тест только один способ получить обратную связь , когда вы создаете программный продукт . В дополнение к юнит-тестам , я хотел проверить интеграцию различных компонентов . Так как большинство людей тестирующих насыщенных приложений JavaScript , я потянулся к матери всех инструментов тестирования, Selenium.


Теперь , прежде чем я колотить его , без надлежащего введения , стоит отметить, что Selenium является отличным способом , чтобы убедиться что всё веб-приложение работает с Full production , как c базой данных так и всех ваших development зависимостей и т.д. И с точки зрения обеспечения качества , Selenium может быть мощным инструментом для команды, нуждающихся end-to-end приемочные испытаний пользовательского интерфейса.


Но с течением времени, казалось бы, небольшие тесты построенные на Selenium могут превратить скорость вашей команды, в темпе улитки . Есть один простой способ , уменьшить эту боль , во первых избегайте разработку большого приложения . Если вы разрабатываете горсть мелких веб-приложений вместо одного большого, это может помочь держать вас на плаву немного дольше, because no individual build will crush the team, as you grow.


Но даже на небольшом проекте , реальная проблема с Selenium является то, что он не является частью процесса TDD . Когда я делаю red/ green/ refactor у меня нет времени для медленной обратной связи в любой форме . Мне нужен был способ, чтобы написать модуль и интеграционные тесты, которые обеспечат быструю обратную связь, чтобы помочь мне формировать программы которые я написал в более итерационным образом . Если вы используете версию Ember.js > = RC3 , вам повезло , потому что писать модульный тест является прогулкой по частям данной статьи .



Установка Загрузчика тестов


Теперь, когда мы можем написать JavaScript тесты для нашего приложения , как мы можем запускать их? Большинство разработчиков могут начать непосредственно с помощью браузера, а потому, что я хотел что-то такое, чтобы я мог запускать тесты командной строки в CI среде с богатой экосистемой плагинов , я присмотрел плагин Karma .

Что мне понравилось в Карме, это то, что он только запускает ваши тесты. Неважно какой вы используете JavaScript фреймфорк для тестирования и клиент сайд MVC фреймворк. Всё просто, достаточно наапись Тесты , которые выполняются в на вашем продакшн Ember.js нужно всего лишь написать несколько строк конфигурации.

Но прежде, чем настроить карму , мы должны установить его с помощью npm. Я рекомендую установить его локально, поэтому вы можете сохранить ваши npm модули изолировано на проект . Чтобы сделать это, добавьте файл с именем package.json 'в корневом каталоге вашего проекта , который выглядит примерно так:

{
 "dependencies": {
  "karma-qunit": "*",
  "karma": "0.10.2"
 }
}

В примере выше мы указываем зависимости от карма и плагина для QUnit . После того как вы сохраните файл package.json выше, вернитесь в командную строку и введите npm install чтобы установить необходимые модули Node .

После npm install вы увидите новую папку с именем node_modules в корневом каталоге вашего проекта. Эта папка содержит весь код JavaScript который мы установили с помощью npm, в том числе Karma и плагин для QUnit . Если вы пойдете внутрь каталога node_modules/karma/bin/ вы увидите исполняемый файл Карма . Мы будем использовать конфигурацию загрузчика тестов, чтобы запускать их из командной строки и т.д.

Настройка загрузчика


Далее нам нужно настроить карму поэтому она знает, как запускать QUnit тесты . Введите karma init из корня проекта. Вам будет предложен список вопросов. Первый будет спрашивать, что фреймворк для тестирования вы хотите использовать , нажмите Tab , пока не увидите QUnit, а затем нажмите Enter. На следующий вопрос по поводу Require.js мы не отвечаем, поскольку мы не будем использовать его для этого примера приложения (от переводчика, а стоило бы показать пример использования и Require) . Tab, пока не увидите PhantomJS для третьего вопроса , и вы должны нажать Enter дважды, поскольку это позволяет использовать несколько вариантов здесь . Что касается остальных, просто оставить их по своему усмотрению умолчанию.

Когда вы закончите , вы увидите подготовленный файл конфигурации с именем karma.conf.js в корне вашего проекта. Если вы хотите узнать больше о различных вариантах которые поддерживает Карма, вы можете посетить сайт Кармы. Ради этого примера , у меня есть упрощенная версия файла конфигурации , чтобы помочь новичкам быстрее освоиться.

Если вы хотите следовать за мной , удалите сгенерированый файл конфигурации и заменить его с этим.

module.exports = function(karma) {
 karma.set({
  basePath: 'js',
  files: ["vendor/jquery/jquery.min.js", "vendor/handlebars/handlebars.js", "vendor/ember/ember.js", "vendor/jquery-mockjax/jquery.mockjax.js", "app.js", "tests/*.js"],
  logLevel: karma.LOG_ERROR,
  browsers: ['PhantomJS'],
  singleRun: true,
  autoWatch: false,
  frameworks: ["qunit"]
 });
};

В верхней части файла конфигурации , вы увидите, что я установил BasePath в JS , потому что все библиотеки JavaScript живут именно в этой папке в проекте . Затем я сказал Карме , где она может найти файлы JavaScript , необходимые для тестирования нашего простого приложения . Это включает в себя JQuery, Handlebars , Ember.js и сам файл app.js .

Написание Первого модульного теста


Теперь мы можем добавить первый файл модульного тестирования в проект. Сначала нужно создать новую папку с именем tests и вложить ее в в папку JS. Добавить в нее файл с именем unit_tests.js , что выглядит примерно так .

test('hello world', function() {
 equal(1, 1, "");
});

Этот тест не делает ничего ценного, но это поможет нам проверить правильность работы Кармы. Обратите внимание на файлы в разделе Карма , мы уже добавили каталог js/tests. Таким образом Карма потянет в каждый файл в папке js.

Теперь у нас есть правильно настроенный файл конфигурации, чтобы запустить QUnit тесты из командной строки напишите ./node_modules/karma/bin/karma start.

Если у вас настроено все верно, то вы должны увидеть один выполненный тест который закончился успешно. Чтобы проверить, что тест крашиться. Поправьте тест :

test('hello world', function() {
 equal(1, 2, "");
});


Пример приложения


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



Пример: Приложение имеет три проблемы. Во-первых, мы хотим показать, имя и фамилию пользователя , а не только имя. Далее, при нажатии на кнопку удаления он будет на самом деле не удалить пользователя. И, наконец, при добавлении имени и фамилии нажмите добавить , после этого Новое имя и фамилия не появится в таблице.

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

Модульное тестирование вычисляемых свойств Unit тестирование в ember очень простое, вы просто создаете новый объект и просите fullName значение.

test('fullName property returns both first and last', function() {
 var person = App.Person.create({
  firstName: 'toran',
  lastName: 'billups'
 });
 var result = person.get('fullName');
 equal(result, 'toran billups', "fullName was " + result);
});

Далее если вы вернетесь в командную строку и запустите ./node_modules/karma/bin/karma start он должен показать один неисправный тест с полезным сообщение, описывающее FULLNAME как определено в настоящее время . Чтобы исправить это, мы должны открыть файл app.js и добавить вычисляемое свойство , которая возвращает строку объединенных первой и последней значений имен.

App.Person = Ember.Object.extend({
 firstName: '',
 lastName: '',
 fullName: function() {
  var firstName = this.get('firstName');
  var lastName = this.get('lastName');
  return firstName + ' ' + lastName;
 }.property()
});


Если вы вернетесь к командной строке и запустить ./node_modules/karma/bin/karma start вы должны увидеть тест прохождения блока . Вы можете расширить этот пример , написав несколько других модульных тестов чтобы показать, что вычисленное свойство должно измениться, когда имя или фамилия обновляется от модели.


test('fullName property returns both first and last', function() {
 var person = App.Person.create({
  firstName: 'toran',
  lastName: 'billups'
 });
 var result = person.get('fullName');
 equal(result, 'toran billups', "fullName was " + result);
});
test('fullName property updates when firstName is changed', function() {
 var person = App.Person.create({
  firstName: 'toran',
  lastName: 'billups'
 });
 var result = person.get('fullName');
 equal(result, 'toran billups', "fullName was " + result);
 person.set('firstName', 'wat');
 result = person.get('fullName');
 equal(result, 'wat billups', "fullName was " + result);
});
test('fullName property updates when lastName is changed', function() {
 var person = App.Person.create({
  firstName: 'toran',
  lastName: 'billups'
 });
 var result = person.get('fullName');
 equal(result, 'toran billups', "fullName was " + result);
 person.set('lastName', 'tbozz');
 result = person.get('fullName');
 equal(result, 'toran tbozz', "fullName was " + result);
});


Если вы добавите эти два дополнительных теста и запустить все три из командной строки, у вас должны провалится 2 теста. Чтобы прошли все три теста, измените вычисляемое свойство имя и фамилию. Теперь, если вы запустите ./node_modules/karma/bin/karma start из командной строки, у вас должны пройти успешно все 3 теста .

App.Person = Ember.Object.extend({
 firstName: '',
 lastName: '',
 fullName: function() {
  var firstName = this.get('firstName');
  var lastName = this.get('lastName');
  return firstName + ' ' + lastName;
 }.property('firstName', 'lastName')
});


Добавляем Карма - Ember - Препроцессор и настроим его


Теперь у нас есть вычисляемое свойство в модели, мы должны смотреть на сам шаблон, потому что в настоящее время мы не используем новое свойство FullName . В прошлом, вы должны были бы соединять все сами, или использовать Selenium чтобы проверить корректное отображение данных. Но с Ember-тестированием теперь вы можете интегрировать это, добавив несколько строк JavaScript и плагин для Кармы.

Сначала откройте файл package.json и добавьте karma-ember-preprocessor зависимость. После обновления файла package.json , npm install из командной строки , чтобы скачать зависимость.

{
 "dependencies": {
  "karma-ember-preprocessor": "*",
  "karma-qunit": "*",
  "karma": "0.10.2"
 }
}


Теперь, когда у вас есть пред-процессор установлен, вы должны сообщить Карме о шаблонах. В разделе files вашего karma.conf.js файла добавьте следующие, что скажет Карме о шаблонах Handlebars.

module.exports = function(karma) {
 karma.set({
  basePath: 'js',

  files: [
   "vendor/jquery/jquery.min.js",
   "vendor/handlebars/handlebars.js",
   "vendor/ember/ember.js",
   "vendor/jquery-mockjax/jquery.mockjax.js",
   "app.js",
   "tests/*.js",
   "templates/*.handlebars"
  ],

  logLevel: karma.LOG_ERROR,
  browsers: ['PhantomJS'],
  singleRun: true,
  autoWatch: false,

  frameworks: ["qunit"]
 });
};


Далее нам нужно сообщить Карме , что делать с этими Handlebars файлами, потому что технически мы хотим иметь перекомпилированным каждый шаблон перед его передачей PhantomJS. Добавим конфигурацию препроцессора и укажем, что любой файл с расширением *.handlebars принадлежит Ember-препроцессору . Кроме того, необходимо добавить в конфигурации plugins Эмбер-предпроцессор (наряду с несколькими другими , которые обычно стоят по умолчанию Кармы ).

module.exports = function(karma) {
 karma.set({
  basePath: 'js',

  files: [
   "vendor/jquery/jquery.min.js",
   "vendor/handlebars/handlebars.js",
   "vendor/ember/ember.js",
   "vendor/jquery-mockjax/jquery.mockjax.js",
   "app.js",
   "tests/*.js",
   "templates/*.handlebars"
  ],

  logLevel: karma.LOG_ERROR,
  browsers: ['PhantomJS'],
  singleRun: true,
  autoWatch: false,

  frameworks: ["qunit"],

  plugins: [
   'karma-qunit',
   'karma-chrome-launcher',
   'karma-ember-preprocessor',
   'karma-phantomjs-launcher'
  ],

  preprocessors: {
   "**/*.handlebars": 'ember'
  }
 });
};


Интеграция Тестирование с данными привязанными к данным шаблона


Теперь у нас есть настройки конфигурации Карма для интеграционного тестирования, добавить новый файл с именем integration_tests.js в папке tests . Внутри этой папки нам нужно добавить простой тест, чтобы доказать, что мы можем запустить приложение Ember.js без ошибок. Добавить простой тест QUnit чтобы увидеть, если мы можем попасть в '/' маршрут и получить основной HTML. Для первоначального теста , мы только утверждли , что тег таблица существует в HTML , который был создан .

test('hello world', function() {
 App.reset();
 visit("/").then(function() {
  ok(exists("table"));
 });
});

Обратите внимание, мы используем несколько помощников , которые встроены в Эмбер -тестирование, как посещения и поист. Визит помощник говорит Эмберу какой роут открыть в ходе выполнения теста . Этот тест начинается с '/' маршрута , потому что по нему связана модель и шаблон нашего приложения. Поиск хелпер - это быстрый способ для поиска элементов в DOM с помощью CSS селектора , также как и с JQuery.


Прежде чем мы сможем запустить этот тест нам нужно добавить вспомогательный файл теста, что будет инициализировать хелперы и общий корневой элемент для вывода графики. Добавьте код ниже в файл с именем integration_test_helper.js в том же каталоге тестов . Это будет гарантировать, что наше приложение имеет тестовых помощников во время выполнения.

document.write('
'); App.rootElement = '#ember-testing'; App.setupForTesting(); App.injectTestHelpers(); function exists(selector) { return !!find(selector).length; }

Запустите тест и если он прошел, удалите таблицу из шаблона Handlebars, чтобы добится провала.

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

test('hello world', function() {
App.reset();
visit("/").then(function() {
var rows = find("table tr").length;
equal(rows, 2, rows);
});
});

Примечание: Приложение в настоящее время возвращаются трудно кодированные данные , чтобы держать все просто в данный момент. Если Вам интересно , почему мы получаем два человека , вот метод find:

App.Person.reopenClass({
people: [],
find: function() {
var first = App.Person.create({firstName: 'x', lastName: 'y'});
var last = App.Person.create({firstName: 'x', lastName: 'y'});
this.people.pushObject(first);
this.people.pushObject(last);
return this.people;
}
});

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

document.write('
');

App.rootElement = '#ember-testing';
App.setupForTesting();
App.injectTestHelpers();

function exists(selector) {
 return !!find(selector).length;
}

Если вы запустите тест выше, вы увидеть провальный тест потому что мы еще не обновили шаблон для использования FULLNAME . Теперь, когда у нас есть провальный тест, обновите шаблон для использования FULLNAME и выполнения тестов с использованием старт ./node_modules/karma/bin/karma start . Теперь вы должны иметь набор успешных модульных и интеграционных тестов .

Должен ли я писать юнит или интеграционные тесты?


Если вы спрашиваете себя , " когда я должен написать модульный тест против теста интеграции ? " , Ответ прост : то, что будет менее болезненным? Если писать модульный тест быстрее и это объясняет проблему лучше , чем гораздо больше испытаний интеграции , то я говорю написать модульный тест . Если модульные тесты кажутся менее ценными, поскольку вы делаете основную CRUD и реальное поведение заключается во взаимодействии между компонентами , я говорю написать тест интеграции. Потому что интеграционные тесты, написанные с Ember -тестирования являются невероятно быстро , что они являются частью цикла обратной связи c разработчиком и должен использоваться так же, как модульного тест , когда это имеет смысл.

Чтобы показать CRUD(create, read, update, delete) как тест интеграции в действии, напишите следующий тест.

test('add will append another person to the html table', function() {
  App.Person.people = [];
  App.reset();
  visit("/").then(function() {
   var rows = find("table tr").length
   equal(rows, 2, "the table had " + rows + " rows");
   fillIn(".firstName", "foo");
   fillIn(".lastName", "bar");
   return click(".submit");
  }).then(function() {
    equal(find("table tr").length, 3, "the table of people was not complete");
    equal(find("table tr:eq(2) td:eq(0)").text(), "foo bar", "the fullName for the);
});
});

Начните , сказав тесту , в каком состоянии вы хотите работать, используя помощник FILLIN , добавить имя и фамилию. Теперь, если вы нажмете на кнопку отправки следует добавить , что человек в HTML таблице , поэтому в возвращении можно утверждать, что существуют три человека в HTML таблице. Запустите этот тест , и он выйдет из строя , поскольку контроллер Эмбер не является полным.

Чтобы получить прохождение теста, добавьте следующую строку в PeopleController
App.PeopleController = Ember.ArrayController.extend({
actions: {
addPerson: function() {
var person = {
firstName: this.get('firstName'),
lastName: this.get('lastName')
};
App.Person.add(person);
}
}
});


Теперь, если вы запустите тесты , используя ./node_modules/karma/bin/karma start он должен показать трех человек в оказанную HTML.

Последний тест является удаление - мы находим кнопку для конкретной строки и жмем на нее . В дальнейшем то мы просто проверим одим человеком меньше показано в HTML таблице.

test('delete will remove the person for a given row', function() {
 App.Person.people = [];
 App.reset();
 visit("/").then(function() {
  var rows = find("table tr").length;
  equal(rows, 2, "the table had " + rows + " rows");
  return click("table .delete:first");
 }).then(function() {
  equal(find("table tr").length, 1, "the table of people was not complete
});
});")
 })
})

Чтобы получить этот прохождение , просто добавьте следующую строку в PeopleController :

App.PeopleController = Ember.ArrayController.extend({
actions: {
addPerson: function() {
var person = {
firstName: this.get('firstName'),
lastName: this.get('lastName')
};
App.Person.add(person);
},
deletePerson: function(person) {
App.Person.remove(person);
}
}
});

Запустите тесты из командной строки , и вы должны еще раз иметь проходной набор тестов .


Заключение


Так что завершается наш пример приложения . Не стесняйтесь задавать любые вопросы вниз в комментариях.


Бонус: Но я уже использую Grunt ...


Если вы предпочитаете использовать Грант вместо karma-ember препроцессора, просто удалите конфигурацию плагинов и препроцессоров . Также удалите templates/*.handlebars из раздела файлы так как Карме не нужно будет предварительной компиляции шаблонов. Вот упрощенная karma.conf.js , которая работает при использовании Grunt предварительной компиляции шаблонов handlebars.

module.exports = function(karma) {
 karma.set({
  basePath: 'js',

  files: [
   "lib/deps.min.js", //built by your grunt task
   "tests/*.js"
  ],

  logLevel: karma.LOG_ERROR,
  browsers: ['PhantomJS'],
  singleRun: true,
  autoWatch: false,

  frameworks: ["qunit"]
 });
};


Статья от Toran Billups http://code.tutsplus.com/tutorials/emberjs-testing--net-36575

Комментариев нет:

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