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

AngularJS

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

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

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

– Нет одним ever

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

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

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

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

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

Модульное тестирование улучшает orthogonality.По сути, код называется “ортогональным”, когда это легко изменить.Крепление ошибку или добавив функцию не влечет за собой ничего, кроме изменения поведение кода, как описано в прагматический программист: от подмастерья к 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 )

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

Начальная загрузка среды тестирования для AngularJS

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

Карма: Захватывающий тест 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 __57 | 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

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

Sinon

Sinon 20 5 описывает себя как “автономные испытания шпионы, окурки и издевается для JavaScript.” Spies, заглушки и издевается над всем ответить на тот же вопрос: Как сделатьВы эффективно заменить одно с другим, когда работает тест?Предположим, у вас есть функция, которая принимает другую в качестве параметра и вызывает его.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();
});

it('shou ...

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

Comments are closed.