Javascript 如何监视服务来测试它——AngularJS/Jasmine

Javascript 如何监视服务来测试它——AngularJS/Jasmine,javascript,angularjs,unit-testing,jasmine,code-coverage,Javascript,Angularjs,Unit Testing,Jasmine,Code Coverage,我尝试了我在互联网上找到的一切,但都没有成功。试图在我的服务中测试功能,但根据我的覆盖范围,我从未访问过它。任何帮助都将不胜感激:) 服务: 'use strict'; angular.module('Service').service('configService', function( $rootScope, $http) { var configObj = null; return { getConfig: function() { if (config

我尝试了我在互联网上找到的一切,但都没有成功。试图在我的服务中测试功能,但根据我的覆盖范围,我从未访问过它。任何帮助都将不胜感激:)

服务:

'use strict';

angular.module('Service').service('configService', function(
  $rootScope, $http) {
  var configObj = null;
  return {

    getConfig: function() {
      if (configObj != null) {
        console.log("returning cached config");
        return configObj;
      }
      else {
        return $http.get('conf.json').then(function(res) {
          $http.get(res.confLocation).then(function(
            locationResponse) {
            configObj = locationResponse;
            $rootScope.configObj = configObj;
            console.log($rootScope.configObj);
            return configObj;
          });
        });
      }
    }
  };
});
在我尝试的测试中,从未访问过getConfig

服务测试:

'use strict';
describe('Service: configService', function() {

  // load the controller's module
  beforeEach(module('Service'));

  var configService, $httpBackend, results, tstLocation, tstRes;
  var tstConfig = {
    "confLocation": "local-dev-conf.json"
  };

  var tstConfigEmpty = {};
  var tstConfigObjEmpty = {};

  var tstConfigObj = {
    "AWS": {
      "region": "us-east-1",
      "endpoint": "http://localhost:8133"
    }
  };

  // Initialize the controller and a mock scope
  beforeEach(inject(function(_configService_, _$httpBackend_) {
    inject(function($rootScope) {
      $rootScope.USERNAME = 'TESTER';
      $rootScope.configObj = tstConfigObj;
      $rootScope.locationResponse = tstLocation;
      $rootScope.res = tstRes;
    });

    configService = _configService_;
    $httpBackend = _$httpBackend_;

    //Problem here??
    spyOn(configService, 'getConfig').and.callFake(function() {
      return {
        then: function() {
          return "something";
        }
      };
    });

  }));
  it('should return a promise', function() {
    expect(configService.getConfig().then).toBeDefined();
  });

  it('should test backend stuff', inject(function() {

    results = configService.getConfig(tstConfig);
    $httpBackend.expectGET('conf.json').respond(tstConfig);
    $httpBackend.expectGET('local-dev-conf.json').respond(tstConfigObj);
    $httpBackend.flush();
  }));

  //Thanks Miles
  it('should check if it was called', inject(function() {
    results = configService.getConfig().then();
    expect(configService.getConfig).toHaveBeenCalled();

    });
    // console.log(results);
  }));

  it('should check for a null configObj', inject(function() {
    results = configService.getConfig(tstConfigObjEmpty).then(function() {
      expect(results).toBe(null);
    });
    // console.log(results);
    // console.log(tstConfigObj);
  }));

  it('should check for a non-null configObj', inject(function() {
    results = configService.getConfig(tstConfigObj).then(function() {

      // Any string is accepted right now -- Why??
      expect(results).toEqual("returning cached config");
      expect(results).toBe("returning cached config");
      expect(results).toBe("your mom"); // SHOULDN'T BE WORKING BUT DOES
      expect(results).toEqual("Object{AWS: Object{region: 'us-east-1', endpoint: 'http://localhost:8133'}}");
      expect(results).toBe("Object{AWS: Object{region: 'us-east-1', endpoint: 'http://localhost:8133'}}");
    });
    // console.log(results);
    // console.log(tstConfigObj);
  }));

  it('should check for null file', inject(function() {
    results = configService.getConfig(tstConfigEmpty).then(function() {
      expect(results).toEqual(null);
      expect(results).toBe(null);
    });
  }));

  it('should test a valid file', inject(function() {
    results = configService.getConfig(tstConfig).then(function() {
      expect(results).not.toBe(null);
      expect(results).toEqual("Object{confLocation: 'local-dev-conf.json'}");
    })
});
我认为我使用spyOn是错误的,或者在测试中没有正确访问getConfig。想法

编辑:

编辑2:由于Miles发现了一个问题,更改了测试3,但仍然没有更新测试覆盖率。正如艾米指出的,我的间谍逻辑出了问题。我不应该用电话,看起来是假的

编辑3:由于迈尔斯的帮助,现在可以访问该功能。必须将我的spyOn更改为:

spyOn(configService, 'getConfig').and.callThrough(); 
然后添加测试用例:

results = configService.getConfig(tstConfig).then();   
expect(configService.getConfig).toHaveBeenCalled();

您使用的是哪种版本的茉莉花?Jasmine 2.0中添加了and.callFake语法。也许测试套件只需要指向新版本


您调用的是假函数,而不是函数。因此函数内部的逻辑不会被调用。

这里有一个问题:

results = configService.getConfig(tstConfigObj).then(function() {
  expect(results).toHaveBeenCalled();
  expect(results).toHaveBeenCalledWith(tstConfigObj);
});
getConfig
不接受任何参数,也不接受
then
。忽略这些错误,
results
被分配了
中的字符串“something”,然后
。即使
expect
语句触发,您似乎也在测试是否调用了字符串。请尝试以下方法:

results = configService.getConfig().then();
expect(configService.getConfig).toHaveBeenCalled();

你能详细说明一下吗?我对Angular/Jasmine相当陌生,还不太了解spyOn。谢谢除非您需要测试某个函数是否已被调用,否则不要监视该函数(因此,您可能会在测试中监视此服务中的某个函数,以获取使用该函数的内容)。如果您需要监视它,并且需要运行其中的代码,请使用@Miles P在其评论中所说的
和.callThrough
。感谢您的回复。我实现了这个更改,虽然测试通过了,但它仍然没有提供getConfig的任何覆盖。。。我认为我的spyOn有问题,正如Amy在上面提到的,但我有点不确定如何解决这个问题然后执行
results=configService.getConfig(tstConfig).then()和最后的
expect(configService.getConfig).tohavebeencall()成功了!它终于访问了这个函数,我的覆盖率达到了42%。是时候处理剩下的代码了,非常感谢!根据你发布的代码,我不确定这是胜利。仅仅测试函数是否被调用并不能给你带来任何好处。虽然这个答案在技术上是正确的,但它并不能帮助OP解决实际问题(不理解间谍的作用)。我没有投反对票,但这不是一个好答案,尽管被接受。我使用的是Jasmine 2.4.1。您是否尝试过and.returnValue而不是and.callFake并返回thenable?我尝试过用and.returnValue替换and.callFake,以及:“spyOn(配置服务,'getConfig')。和.returnValue(函数(){return{then:function(){return“something”;“当我这样做时,9个测试中的6个崩溃了。注意,在调用函数后的下一行中,不需要检查函数是否被调用了!