AngularJS:如何在配置阶段使用$ q进行单元测试?

时间:2015-08-11 10:57:12

标签: javascript angularjs unit-testing jasmine promise

我有一个角度服务负责加载 config.json 文件。我想在我的运行阶段中调用它,所以我在我的 $ rootContext 中设置了json,因此,将来每个人都可以使用它。

基本上,这就是我所拥有的:

angular.module('app.core', []).run(function(CoreRun) {
    CoreRun.run();
});

我的CoreRun服务在哪里:

 angular.module('app.core').factory('CoreRun', CoreRun);

 CoreRun.$inject = ['$rootScope', 'config'];

 function CoreRun($rootScope, config) {
   function run() {
     config.load().then(function(response) {
       $rootScope.config = response.data;
     });
   }    
   return {
     run: run
   };
}

这很好用,当我尝试测试时出现问题。我想监视我的配置服务,所以它返回一个假的承诺。但是,我无法做到,因为在我的测试的配置阶段,服务不可用,我不能注入$ q。

据我所知,在配置阶段,我必须模拟我的配置服务,因为它是由运行块调用的。

到目前为止,我找到的唯一方法是使用jQuery产生承诺,我真的不喜欢。

beforeEach(module('app.core'));

var configSample;

beforeEach(module(function ($provide) {
   config = jasmine.createSpyObj('config', [ 'load' ]);
   config.load.and.callFake(function() {
     configSample = { baseUrl: 'someurl' };        
     return jQuery.Deferred().resolve({data: configSample}).promise();
   });
   provide.value('config', config);
}));

it('Should load configuration using the correspond service', function() {
  // assert
  expect(config.load).toHaveBeenCalled();
  expect($rootScope.config).toBe(configSample);
});

有没有办法制定更正确的解决方法?

编辑:可能值得一提的是,这是单元测试运行块时的问题。

1 个答案:

答案 0 :(得分:4)

似乎无法以正确的方式注入$q,因为run()块中的函数会立即触发。 run()块在Angular中被视为配置阶段,因此测试中的inject()仅在配置块之后运行,因此即使您在测试中inject() $q,它也将是{{} 1}},因为undefined首先执行。

经过一段时间后,我能够在run()块中获得$q一个非常脏的解决方法。我们的想法是创建一个额外的角度模块,并在应用程序模块之前将其包含在测试中。这个额外的模块也应该有一个module(function ($provide) {})块,它将run()发布到全局命名空间。 Injector将首先调用额外的模块$q,然后调用app模块的run()

run()

这个额外的模块可以作为spec文件之前的测试套件的单独文件包含在内。如果您将模块放在测试所在的同一文件中,那么您不需要使用全局angular.module('global $q', []).run(function ($q) { window.$q = $q; }); describe('test', function () { beforeEach(function () { module('global $q'); module('app.core'); module(function ($provide) { console.log(window.$q); // exists }); inject(); }); }); 变量,而只需要使用文件中的变量。

Here is a working plunker(参见" script.js"文件)

第一个解决方案(不解决问题):

在这种情况下,您实际上可以使用window,但您必须将其注入测试文件。在这里,您不会将它真正地注入到被测单元中,而是直接注入测试文件以便能够在测试中使用它。所以它实际上并不取决于被测单元的类型:

$q

资源:

  • 详细了解// variable that holds injected $q service var $q; beforeEach(module(function ($provide) { config = jasmine.createSpyObj('config', [ 'load' ]); config.load.and.callFake(function() { var configSample = { baseUrl: 'someurl' }; // create new deferred obj var deferred = $q.defer(); // resolve promise deferred.resolve({ data: configSample }); // return promise return deferred.promise; }); provide.value('config', config); })); // inject $q service and save it to global (for spec) variable // to be able to access it from mocks beforeEach(inject(function (_$q_) { $q = _$q_; })); - angular.mock.inject
  • $q

还有一个注意事项: config 阶段和运行阶段是两个不同的事情。 Config 块仅允许使用提供程序,但在 run 块中,您可以注入几乎所有内容(提供程序除外)。更多信息 - Module Loading & Dependencies