Как создать модульный тест с жасмином при использовании angular-datatables

Я использую angular-datatables в своем проекте, и я хотел бы написать для него модульный тест Jasmine/Karma.

Это код из моего контроллера:

 $scope.dtOptions = DTOptionsBuilder.fromSource('/api/books/')
                .withBootstrap()
                .withPaginationType('simple_numbers')
                .withDisplayLength(10)
                //.withOption('serverSide', true)
                .withOption('processing', true)
                .withOption('createdRow', createdRow);

            $scope.dtColumns = [
                DTColumnBuilder.newColumn('id').withTitle('ID'),
                DTColumnBuilder.newColumn('name').withTitle('Name')
                DTColumnBuilder.newColumn(null).withTitle('Actions').notSortable()
                    .renderWith(actionsHtml)
            ];

Как мне теперь написать для него модульный тест, подделав ответ JSON от /api/books?

2 ответа

var $scope, $state, DTColumnBuilder, DTOptionsBuilder, createController, $httpBackend;

beforeEach(function () {
  DTColumnBuilder  = {};
  DTOptionsBuilder = {};
  $state           = {};
  $httpBackend     = {};

  module('app', function ($provide) {
    $provide.value('$state', $state);
    $provide.value('$httpBackend', $httpBackend);
    $provide.value('DTColumnBuilder', DTColumnBuilder);
    $provide.value('DTOptionsBuilder', DTOptionsBuilder);
  });

  inject(function ($controller, $injector) {
    $scope = $injector.get('$rootScope').$new();
    $state = $injector.get('$state');
    $httpBackend = $injector.get('$httpBackend');
    DTColumnBuilder  = $injector.get('DTColumnBuilder');
    DTOptionsBuilder = $injector.get('DTOptionsBuilder');

    aliasOfYourController = function () {
      return $controller('originalNameOfController', {
        $scope: scope,
        $state: $state,
        DTOptionsBuilder: DTOptionsBuilder,
        DTColumnBuilder: DTColumnBuilder        
      });
    }

    spyOn($state, 'go');

    $httpBackend.flush();
  });

  afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
  });

  // Stub out the methods of interest. 
  DTOptionsBuilder.fromSource = angular.noop;
  DTColumnBuilder.bar = function () { return 'bar'; };
});

Природа spy это то, что позволяет исходной реализации делать свое дело, но записывает все вызовы указанной функции и ассортимент связанных данных.

stub с другой стороны, это spy с расширенным API, где вы можете полностью изменить работу указанной функции. Возвращаемое значение, ожидаемые параметры и т. Д.

Предполагая, что мы использовали вышеупомянутый блок beforeEach, DTOptionsBuilder.fromSource будет noop с этой точки зрения. Таким образом, было бы безопасно spy и ожидаем, что метод был вызван.

it('should have been called', function () {
  var spy = spyOn(DTOPtionsBuilder, 'fromSource');
  aliasOfYourController();
  expect(spy).toHaveBeenCalled();
});

Если бы вы хотели манипулировать возвращаемым значением указанной функции, я бы взял sinonjs и сделал его stub,

it('became "foo"', function () {
  DTOptionsBuilder.fromSource = sinon.stub().returns('foo');
  aliasOfYourController();
  expect($scope.dtOptions).toEqual('foo');
});

Теперь, так как вы работаете с обещаниями, это немного сложнее, но основами для исключения функции, основанной на обещаниях, будет:

  • впрыскивать $q в ваш файл спецификаций.
  • Скажи stub возвращать $q.when(/** value **/) в случае разрешенного обещания.
  • Скажи stub возвращать $q.reject(/** err **/) в случае отклоненного обещания.
  • Бежать $timeout.flush() сбросить все отложенные задачи. И если вы издеваетесь над HTTP-ответом, который вы используете $httpBackend в ваших юнит-тестах и $httpBackend.flush() сбросить все отложенные задачи.
  • Вызвать done обратный вызов, чтобы уведомить Jasmine о том, что вы закончили ожидание асинхронных задач (может не потребоваться). Это зависит от тестовой среды / бегуна.

Это может выглядеть примерно так:

it('resolves with "foo"', function (done) {
  DTOptionsBuilder.fromSource = sinon.stub().returns($q.when('foo'));
  expect($scope.options).to.eventually.become('foo').and.notify(done); // this is taken from the chai-as-promised library, I'm not sure what the Jasmine equivalent would be (if there is one).
  aliasOfYourController();
  $timeout.flush();
});

И, если вы хотите проверить $state.go('toSomeState') тогда юнит-тест может быть:

   it('should redirected successfully', function() {
       var stateParams = {
          id: 22,
          name: sample
       }
       functionNameInsideWhichItsBeenCalled(stateParams);
       expect($state.go).toHaveBeenCalledWith('toSomeState', {
           id: stateParams.id,
           name: stateParams.name
       });
   });

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

Чтобы смоделировать ответ http, вы используете $httpBackend в своих модульных тестах.

используйте $httpBackend.expect, если вы хотите, чтобы тест не прошел, если запрос не сделан. Вы также можете определить ложный ответ.

используйте $httpBackend.when, когда вы хотите определить ложный ответ, если запрос сделан, но не делает запрос обязательным.

КОД ОБЪЯСНИТЬ...

КОД КОНТРОЛЛЕРА ДЛЯ ТЕСТИРОВАНИЯ

self.callme = function() {
    $http.get('/mypath').then(function() {
        console.log("I will be executed when flush is called if the mock API response is 2xx");
    }).catch(function() {
        console.log("I will be executed when flush is called if the mock API response is NOT 2xx");
    });
}

КОД ТЕСТА БЛОКА...

$httpBackend.expectGET('/mypath').respond(200, {data:"fred"});
controller.callme();
// The THEN code has not been executed here
$httpBackend.flush();
// The THEN success code is now executed because we responded with 2xx (200)
Другие вопросы по тегам