Введение в модульное тестирование В AngularJS приложений

AngularJS

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

Одна из причин успеха AngularJS “является его незаурядные способности к тестированию.Это решительно поддержали Karma 11 2 (зрелищным тест бегун, написанная Войта Jina) и его многочисленные плагины.Карма, в сочетании с его товарищи Mocha 17 3 , Chai 18 4 и Sinon 20 5 , предлагает полный набор инструментов для производства качественного кода, который легко поддерживать, ошибка-бесплатно и хорошо документированы .

“Ну, я просто запустить приложение и посмотреть, все ли работает.Мы никогда не было никаких проблем сделать это “.не

– Нет одним ever

Основным фактором, который заставил меня перейти от “Ну, я просто запустить приложение и посмотреть, все ли работает” в том, что, в первый раз, я мог бы __54 “У меня есть юнит-тесты!” | Сосредоточьтесь на том, matters и о том, что я люблю в программировании: создание интеллектуальных алгоритмов и красивые интерфейсы .

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

Слово о единице Testing

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

Если вы знакомыс модульного тестирования, то вы уже знаете, насколько уверенно разработчик чувствует, когда рефакторинга протестированного кода.Если вы не знакомы, то представьте себе, чтобы избавиться от стресса развертывания, “код-и-молись” стиль кодирования и нескончаемый развитие функций.Большая часть?Это автоматический .

Модульное тестирование улучшает orthogonality.По сути, код называется “ортогональным”, когда это легко изменить.Крепление ошибку или добавив функцию не влечет за собой ничего, кроме изменения поведение кода, как описано в Прагматический Программист: От Journeyman к Master 6 .Юнит-тесты значительно улучшить ортогональность кода, заставив вас написать модульные логические единицы, а большой код куски .

Модульное тестирование также предоставляет вам документации, которая всегда в курсе и что информирует вас о намерениях Кодекса и функционального поведения.Даже если метод имеет загадочное название — что плохо, но мы не будем получать в том, что здесь — вы сразу знаете, что он делает, читая свой тест .

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

Кроме того, модульное тестирование открывает дверь, чтобы проверить приводом development.Хотя это не тема этой статьи, я не могу подчеркнуть достаточно, что развитие тестами является прекрасным и продуктивный способ, чтобы написать код .

Что и чего не Test

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

Everything is bricks, regardless of the level you are at. 7
Все кирпичи, независимо от уровня вы находитесь.( Просмотр большой version 8 )

Все эти кирпичи общий атрибут.Они ведут себя, как черныйкоробки, что означает, что они имеют внутреннюю поведение и внешний интерфейс материализуется входов и выходов.Это именно то, что модульные тесты для: для внешних интерфейсов тестовых кирпичей “.

Black box model (well, this one is gray, but you get the idea). 9
Модель Черный ящик (ну, это один серый, но вы получите идею) ( Просмотр большой version 10 )

Не обращая внимания на внутренние, насколько это возможно, считается хорошей практикой.Модульное тестирование — и тестирование в общем — является сочетание стимулов и реакций .

Бутстрапирование Test Окружающая среда для AngularJS

Чтобы установить достойную среду тестирования для вашего AngularJS приложения, вы будете нуждаться в себя несколько модулей NPM.Давайте быстрый взгляд на них .

Репутация: Spectacular Тест Runner

Karma 11 2 является двигатель, который работает тесты на код.Хотя она была написана для AngularJS, это не специально привязаны к нему и может быть использован для любого приложения JavaScript.Это настраиваемый через файл JSON и использования различных плагинов .

If you don’t see this at some point in this process, then you might have missed something. 12
Если вы не видите это в какой-то момент в этом процессе, то вы, возможно, пропустили что-то.( Просмотр большой version 13 )

Все примеры, приведенные в этой статье, можно найти в посвящена GitHub project 14 наряду с следующий файл конфигурации для кармы .

// Karma configuration
// Generated on Mon Jul 21 2014 11:48:34 GMT+0200 (CEST)
module.exports = function(config) {
  config.set({

    // base path used to resolve all patterns (e.g. files, exclude)
    basePath: '',

    // frameworks to use
    frameworks: ['mocha', 'sinon-chai'],

    // list of files / patterns to load in the browser
    files: [
      'bower_components/angular/angular.js',
      'bower_components/angular-mocks/angular-mocks.js',
      'src/*.js',
      'test/*.mocha.js'
    ],

    // list of files to exclude
    exclude: [],

    // preprocess matching files before serving them to the browser
    preprocessors: {
      'src/*.js': ['coverage']
    },

    coverageReporter: {
      type: 'text-summary',
      dir: 'coverage/'
    },

    // test results reporter to use
    reporters: ['progress', 'coverage'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests on file changes
    autoWatch: true,

    // start these browsers
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};

Этот файл может быть автоматически генерируется, набрав karma init в окне терминала.Доступные ключи описанные в Кармыdocumentation 15 .

Обратите внимание, как источники и тестовые файлы были объявлены.Существует также новичок: ngMock 16 (т.е. angular-mocks.js).ngMock является модуль AngularJS, что обеспечивает несколько утилит тестирования (подробнее об этом в конце этой статьи) .

Mocha

Mocha 17 3 является система тестирования для JavaScript.Он обрабатывает тестов и тестов, и он предлагает хорошие возможности отчетности.Он использует декларативный синтаксис в гнездо ожиданий в случаях и люксы.Давайте посмотрим на следующем примере (бесстыдно похищенной с домашней страницы Mocha): стандарт

describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal(-1, [1,2,3].indexOf(5));
      assert.equal(-1, [1,2,3].indexOf(0));
    });
  });
});

Вы можете видеть, что все тесты содержится в describe вызова.Что интересно гнездования вызовов функций в этом случае является то, что Тесты следовать structure.Здесь Array Набор состоит только из одного подсвитой, #indexOf.Могут быть добавлены другие, конечно.Это подсвита состоит из одного случая, который сам по себе содержит два утверждения и ожидания.Организация наборов тестов в единое целое имеет важное значение.Это гарантирует, что ошибки тестирования будут представлены со значимыми сообщений, таким образом облегчая процесс отладки .

Chai

Мы видели, как Mocha тестовый ванной и тест-тематические возможности для JavaScript. Chai 18 4 , со своей стороны, предлагает различные способы проверки things в тестовых случаях.Эти проверки выполняются с помощью так называемых “утверждения” и в основном отмечают тестовый случай как не удалось и не передаются. Документация Chai имеет more 19 на различных утверждений стилей .

Sinon

Sinon 20 5 описывает себя как “автономные испытания шпионы, заглушки и издевается над для JavaScript.” Шпионов заглушки и издевается над всем ответить на тот же вопрос: Как сделатьВы эффективно заменить одно с другим при запуске теста?Предположим, у вас есть функция, которая принимает еще один в качестве параметра и вызывает его.Sinon предоставляет интеллектуальный и краткий способ контролировать ли называется функция и многое другое (с которым аргументы, сколько раз и т.д.). .

Модульное тестирование на прикладном Level

Точка внешнего интерфейса модуля в AngularJS приложения является его способность бытьвводят в другой модуль —, что он существует и имеет действительный определение .

beforeEach(module('myAwesomeModule'));

Это достаточно, и выдаст ошибку, если myAwesomeModule нигде не найти .

Модульное тестирование в модуле Level

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

Фильтры, услуги и заводы: история зависимостей Injection

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

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

Что такое большое о пути AngularJS “инъекций зависимостей является то, что насмешливый кусок зависимостей код и инъекционных вещи в тестовых супер-легкий.На самом деле, я даже не уверен, что это может быть проще.Давайте рассмотрим эту весьма полезную завод:

angular.module('factories', [])
.factory('chimp', ['$log', function($log) {
  return {
    ook: function() {
      $log.warn('Ook.');
    }
  };
}]);

Посмотрите, как $log впрыскивается, вместо стандартного console.warn?В то время как AngularJS не будет печатать $log заявления в консоли Кармы, избежать побочных эффектов в юнит-тестов настолько, насколько возможно.Я как-то уменьшить на половину длительности юнит-тестов заявку путем насмешливый __30 отслеживания HTTP запросов | которые были все молча неудачу в локальной среде, очевидно .

describe('factories', function() {

  beforeEach(module('factories'));

  var chimp;
  var $log;

  beforeEach(inject(function(_chimp_, _$log_) {
    chimp = _chimp_;
    $log = _$log_;
    sinon.stub($log, 'warn', function() {});
  }));

  describe('when invoked', function() {

    beforeEach(function() {
      chimp.ook();
    });

    it('should say Ook', function() {
      expect($log.warn.callCount).to.equal(1);
      expect($log.warn.args[0][0]).to.equal('Ook.');
    });
  });
});

Образец для тестирования фильтров, услуг или других инъекций является то же самое.Контроллеры могут быть немного сложнее, чтобы проверить, хотя, как мы увидим сейчас .

Controllers

Тестирование контроллера может привести к некоторой путанице.Что мы испытываем?Давайте сосредоточимся на том, что контроллер должен делать.Вы должны быть использованы для рассмотрения любой проверенный элемент как черный ящик в настоящее время.Помните, что AngularJS является модель-представление-то, что (MVW) база, которая является своеобразной иронии судьбы, потому что один из немногих способов определения что-либо в AngularJS приложения использовать ключевое слово controller.Тем не менее, любой вид приличный контроллер, как правило, выступает в качестве прокси между моделью и представлением, через объекты в одну сторону, а обратного вызова в другой .

Контроллер, как правило, настраивает вид при помощи некоторых государственных объектов, таких как следующий (для гипотетического текстового редактирования приложения):

angular.module('textEditor', [])

.controller('EditionCtrl', ['$scope', function($scope) {
  $scope.state = {toolbarVisible: true, documentSaved: true};
  $scope.document = {text: 'Some text'};

  $scope.$watch('document.text', function(value) {
    $scope.state.documentSaved = false;
  }, true);

  $scope.saveDocument = function() {
    $scope.sendHTTP($scope.document.text);
    $scope.state.documentSaved = true;
  };

  $scope.sendHTTP = function(content) {
    // payload creation, HTTP request, etc.
  };
}]);

Скорее всего, что государство будет изменяться как вид и контроллер.toolbarVisible атрибут будет переключаться, скажем, кнопки и клавиши быстрого вызова.Юнит-тесты не должны проверить взаимодействие между видом и остальной Вселенной;это то, что из конца в конец тесты для .

documentSaved значение будет в основном обрабатываются контроллером, однако.Давайте проверим это .

describe('saving a document', function() {

var scope;
var ctrl;

beforeEach(module('textEditor'));

beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('EditionCtrl', {$scope: scope});
}));

it('should have an initial documentSaved state', function(){
expect(scope.state.documentSaved).to.equal(true);
});

describe('documentSaved property', function() {
beforeEach(function() {
// We don't want extra HTTP requests to be sent
// and that's not what we're testing here.
sinon.stub(scope, 'sendHTTP', function() {});

// A call to $apply() must be performed, otherwise the
// scope's watchers won't be run through.
scope.$apply(function () {
scope.document.text += ' And some more text';
});
});

it('should watch for document.text changes', function() {
expect(scope.state.documentSaved).to.equal(false);
});

describe('when calling the saveDocument function', function() {
beforeEach(function() {
scope.saveDocument() ...

Если вы хотите прочитать полностью статью, посетите сайт наших спонсоров

Comments are closed.