在控制器中测试承诺

时间:2017-08-30 10:35:52

标签: javascript angularjs sinon

AngularJS 1.4.8

加载视图时,我在控制器中执行了以下方法。 Watcher是注入控制器的工厂。方法.list()返回bluebird promise。

Watcher.list()
  .then((response) => {
    $scope.watchers = response;
  })
  .catch(notify.error);

我想像这样测试$scope.watchers

it('watchers have been loaded', function () {
  expect($scope.watchers.length).to.equal(2);
});

但收到以下错误:

Chrome 59.0.3071 (Linux 0.0.0) theApp watchersController watchers have been loaded FAILED
        Error: expected 0 to equal 2

目前,sinon stub曾尝试模拟Wacther.list()响应,请查看下面的完整测试文件。

import moment from 'moment';
import sinon from 'auto-release-sinon';
import Promise from 'bluebird';
import ngMock from 'ng_mock';
import expect from 'expect.js';
import _ from 'lodash';

import '../watchersController';

describe('watchersController', function () {
  let $httpBackend;
  let $scope;
  let $route;
  let Watcher;

  const init = function () {
    ngMock.inject(function ($rootScope, $controller, _$httpBackend_, _$route_, _Watcher_) {
      $scope = $rootScope;
      $route = _$route_;
      $httpBackend = _$httpBackend_;
      Watcher = _Watcher_;

      sinon.stub(Watcher, 'list', () => {
        return Promise.resolve([
          { id: '123' },
          { id: '456' }
        ]);
      });

      $route.current = {
        locals: {
          currentTime: moment('2016-08-08T11:56:42.108Z')
        }
      };

      $controller('WatchersController', {
        $scope,
        $route,
        $uibModal: {}
      });

      $scope.$apply();

    });
  };

  beforeEach(function () {
    init();
  });

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

  it('watchers have been loaded', function () {
    expect($scope.watchers.length).to.equal(2);
  });

});

3 个答案:

答案 0 :(得分:0)

在承诺解决后,您需要致电$scope.$apply。像这样的东西应该做的伎俩

import moment from 'moment';
import sinon from 'auto-release-sinon';
import ngMock from 'ng_mock';
import expect from 'expect.js';
import _ from 'lodash';

import '../watchersController';

describe('watchersController', function () {
  let $httpBackend;
  let $scope;
  let $route;
  let Watcher;
  let deferred;

  const init = function () {
    ngMock.inject(function ($rootScope, $controller, _$httpBackend_, _$route_, _Watcher_, _$q_) {
      $scope = $rootScope;
      $route = _$route_;
      $httpBackend = _$httpBackend_;
      Watcher = _Watcher_;
      deferred = _$q_.defer(); // create a deferred

      // let stub return a promise to be resolved later
      sinon.stub(Watcher, 'list', () => deferred.promise);

      $route.current = {
        locals: {
          currentTime: moment('2016-08-08T11:56:42.108Z')
        }
      };

      $controller('WatchersController', {
        $scope,
        $route,
        $uibModal: {}
      });

    });
  };

  beforeEach(function () {
    init();
  });

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

  it('watchers have been loaded', function () {
    deferred.resolve([
          { id: '123' },
          { id: '456' }
    ]) // resolve deferred with mock data

    $scope.$apply(); // apply changes
    expect($scope.watchers.length).to.equal(2);
  });

});

此外,您可能希望通过在另一个测试中调用deferred.reject来检查控制器处理承诺拒绝。

答案 1 :(得分:0)

我们可以在Watcher.list()承诺后在浏览器回调队列中推送测试功能,例如,使用setTimeout

  it('watchers have been loaded', function (done) {
    setTimeout(function () { // catch promise response
      expect($scope.watchers.length).to.equal(2);
      done();
    }); 
  }); 

现在,在从回调队列返回Watcher.list()承诺后执行测试。

setTimeout工作的示例:

console.log('Hi');
setTimeout(function cb1() { 
  console.log('cb1');
}, 5000);
console.log('Bye');

enter image description here

Read more about it.

答案 2 :(得分:0)

在该承诺结束之前,您正在阅读列表。 实际上,您的流程如下:

  • 导入承诺
  • 的班级
  • 读取变量(尚未填充)
  • 测试其值

请参阅以下示例:

function MockedClass() {
	
  var _list = Promise.resolve([
          { id: '123' },
          { id: '456' }
        ]);
        
  var _watchersList = null;
  var _self = this;
  
	this.fillList = function() {
  	_list.then(function(response){
      _self.watchersList = response;
    });
  }
  
  this.getList = function() {
  	return self._watchersList;
  }
}

(function testPromise() {
    console.log("Start");
    
    var p1 = Promise.resolve([
          { id: '123' },
          { id: '456' }
        ]);
    
    var mockedClass = new MockedClass();
    console.log("The list isn't ready: ", mockedClass.getList());
    mockedClass.fillList();
    console.log("The list isn't yet ready: ", mockedClass.getList());
    
    console.log("End");
})();

在上面的示例中,我们应该等待从getList()方法返回的值。

因此,为了实现这一点,我们可以创建一个函数,尝试读取从getList()方法返回的值,但仍然可以进行最多十次尝试。

请参阅以下工作示例:

var attempts = 0;

function MockedClass() {
	
  var _list = Promise.resolve([
          { id: '123' },
          { id: '456' }
        ]);
        
  var _watchersList = null;
  var _self = this;
  
	this.fillList = function() {
  	_list.then(function(response){
      _self.watchersList = response;
    });
  }
  
  this.getList = function() {
  	return _self.watchersList;
  }
}

(function testPromise() {
    console.log("Start");
    
    var p1 = Promise.resolve([
          { id: '123' },
          { id: '456' }
        ]);
    
    var mockedClass = new MockedClass();
    console.log("The list isn't ready: ", mockedClass.getList());
    mockedClass.fillList();
    
    onListReady(mockedClass, function(){
    	console.log("The list is ready: ", mockedClass.getList());
    });
    
    console.log("End");
})();

function onListReady(mockedClass, callback) {
		attempts++;
    if(mockedClass.getList()===undefined && attempts<=10) {
    		console.log("waiting... attempt: " + attempts);
        setTimeout(function(){onListReady(mockedClass, callback)}, 50);
    } else {
    	callback();
    }
}

我希望它可以帮助你,再见。