匿名回调函数中的 Jasmine spyOn 对象

Jasmine spyOn objects inside anonymous callback function

我需要对 controller 函数saveSettings() 进行单元测试,如下所示。我想测试 Session.setUrl() 是否被调用成功。我怎样才能做到这一点?

angular.module("test.controllers")
    .controller('LoginCtrl', function($scope, Validator, Session, Notify){

        $scope.apiUrl = "http://localhost:8080/web-server/";

        $scope.saveSettings = function () {

             Validator.validateUrl($scope.apiUrl, 
                  function (url) { // success callback function
                      Session.setUrl(url);
                  }, 
                  function (error) { // failure callback function
                      Notify.alert('' + error);
                  }
            );
        };
    });

服务定义如下:

angular.module('test.services')
  .service('Validator', function () {

      this.validateUrl = function(url, success, error) {

          if ( !url ) {

              error("URL not found!");
          } else {

               if (url.startsWith('http') && !url.startsWith('https')) {
                   url = url.replace('http', 'https');
               }
               success(url);
          }
      };
  });

在下面的测试代码中(由@Etse 提供),Session.setUrl() 没有被调用:

describe('LoginCtrl', function(){
    beforeEach(module('test.controllers');
    beforeEach(inject($controller){
        this.scope = {};
        this.controller = $controller('LoginCtrl', {$scope: this.scope});
    });

    it('should call setUrl on success', inject(function(Session){
        spyOn(Validator, 'validateUrl');
        spyOn(Session, 'setUrl');
        this.scope.saveSettings();
        expect(Validator.validateUrl).toHaveBeenCalled();
        expect(Session.setUrl).toHaveBeenCalled();
    });
})

已解决 通过添加 callThrough() 如下:

spyOn(Validator, "validateUrl").and.callThrough();

我假设会话是一个服务,因为你正在使用依赖注入来获取它?应该可以只在服务上添加一个普通的间谍——只要你将它也注入到测试中。

在我的测试中,我只是设置了控制器,并在 this-conext 上公开了范围 - 这样我就可以在我的测试中访问它。

我在代码中发现了一些错误,并重写了它。

  • 我发现了一些遗漏的括号
  • $scope 未注入控制器
  • 添加了模块依赖项

这是一个工作代码示例:

angular.module('test.services', [])
  .service('Session', function(){
    this.setUrl = function() {
       console.log("test");
    };
  })
  .service('Validator', function () {
      this.validateUrl = function(url, success, error) {
          if ( !url ) {
              error("URL not found!");
          } else {
               if (url.startsWith('http') && !url.startsWith('https')) {
                   url = url.replace('http', 'https');
               }
               success(url);
          }
      };
  });

angular.module("test.controllers", ['test.services'])
    .controller('LoginCtrl', function($scope, Validator, Session){
        $scope.apiUrl = "http://localhost:8080/web-server/";
        $scope.saveSettings = function () {
             Validator.validateUrl($scope.apiUrl, 
                  function (url) { // success callback function
                      Session.setUrl(url);
                  }, 
                  function (error) { // failure callback function
                      console.log("notify");
                  }
            );
        };
    });


//--- SPECS -------------------------
describe('LoginCtrl', function(){
    beforeEach(function(){
      module("test.controllers");
    });

    beforeEach(inject(function($controller){
        this.scope = {};
        this.controller = $controller('LoginCtrl', {$scope: this.scope});
    }));

    it('should call setUrl on success', inject(function(Session){
        spyOn(Session, 'setUrl');
        this.scope.saveSettings();
        expect(Session.setUrl).toHaveBeenCalled();
    }));
});
<script src="http://searls.github.io/jasmine-all/jasmine-all-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular-mocks.js"></script>