如何使用 Sinon 测试在承诺中设置的 AngularJS 控制器值

How to test an AngularJS controller value that is set within a promise using Sinon

我在测试在服务返回的承诺中设置的控制器值时遇到麻烦。我正在使用 Sinon 对服务进行存根(Karma 用于 运行 测试,Mocha 作为框架,Chai 用于断言)。

与理解问题相比,我对快速修复更感兴趣。我已经阅读了很多,并且在代码和测试下方有一些笔记。

这是代码。

.controller('NavCtrl', function (NavService) {
  var vm = this;
  NavService.getNav()
    .then(function(response){
      vm.nav = response.data;
    });
})

.service('NavService', ['$http', function  ($http) {
  this.getNav = function () {
    return $http.get('_routes');
  };
}]);

这是测试:

describe('NavCtrl', function () {

  var scope;
  var controller;
  var NavService;
  var $q;

  beforeEach(module('nav'));

  beforeEach(inject(function($rootScope, $controller, _$q_, _NavService_){
    NavService = _NavService_;
    scope = $rootScope.$new();
    controller = $controller;
  }));

  it('should have some data', function () {

    var stub = sinon.stub(NavService, 'getNav').returns($q.when({
      response: {
        data: 'data'
      }
    }));

    var vm = controller("NavCtrl", {
      $scope: scope,
      NavService: NavService
    });

    scope.$apply();

    stub.callCount.should.equal(1);
    vm.should.be.defined;
    vm.nav.should.be.defined;

  });

});

正在调用存根,即测试通过,并且定义了 vm,但是 vm.nav 从未获取数据并且测试失败。我认为,我如何处理残存的承诺是罪魁祸首。一些注意事项:

最后,我可能是错的:存根和 promise 可能不是问题,它是不同的东西 and/or 很明显我错过了。

非常感谢任何帮助,如果我可以澄清以上任何内容,请告诉我。

抱歉,您只需要快速修复即可:

var stub = sinon.stub(NavService, 'getNav').returns($q.when({
  response: {
    data: 'data'
  }
}));

您的承诺已解析为包含 response.data 的对象,而不仅仅是 data 检查根据您的代码创建的这个 plunk:https://plnkr.co/edit/GL1Xuf?p=preview

扩展答案

我也经常落入同样的陷阱。于是我开始单独定义一个方法返回的结果。然后,如果该方法是异步的,我将此结果包装在像 $q.when(stubbedResult) 这样的承诺中,这使我可以轻松地 运行 对实际结果的期望,因为我将存根结果保存在变量中,例如

it('Controller should have some data', function () {

    var result = {data: 'data'};
    var stub = sinon.stub(NavService, 'getNav').returns($q.when(result));

    var vm = controller(/* initController */);

    scope.$apply();

    stub.callCount.should.equal(1);
    vm.nav.should.equal(result.data)
})

另外一些测试调试技巧也会派上用场。最简单的方法是在控制台上转储一些数据,以检查某处返回的内容。当然最好使用实际的调试器。

如何快速发现这些错误:

    1. $rootScope.apply() 行放置一个断点(就在执行之前)
    1. 在控制器的 NavService.getNav().then 处理程序中放置一个断点,以查看它是否被调用以及调用的内容
    1. 继续使用调试器执行 $rootScope.$apply() 行。现在调试器应该会命中上一步设置的断点 - 就是这样。

我认为你应该使用 chai-as-promised

然后断言像

这样的承诺
  doSomethingAsync().should.eventually.equal("foo");

或者使用异步等待

 it('should have some data', async function () {

    await scope.$apply();

  });

您可能需要移动然后 getNav() 调用 init 有点函数,然后针对该 init 函数进行测试