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

jbe*_*nal 6 javascript unit-testing promise jasmine angularjs

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

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

angular.module('app.core', []).run(function(CoreRun) {
    CoreRun.run();
});
Run Code Online (Sandbox Code Playgroud)

我的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
   };
}
Run Code Online (Sandbox Code Playgroud)

这工作正常,当我尝试测试它时出现问题.我想监视我的配置服务,所以它返回一个假的承诺.但是,我无法做到,因为在我的测试的配置阶段,服务不可用,我不能注入$ 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);
});
Run Code Online (Sandbox Code Playgroud)

有没有办法做出更正确的解决方法?

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

Mic*_*nov 4

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

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

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();

    });
});
Run Code Online (Sandbox Code Playgroud)

这个额外的模块可以作为测试套件的单独文件包含在规范文件之前。如果将模块放在测试所在的同一文件中,则不需要使用全局window变量,而只需使用文件中的变量。

这是一个正在运行的插件(请参阅“script.js”文件)

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

您实际上可以$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_;
}));
Run Code Online (Sandbox Code Playgroud)

资源:

还有一点要注意:配置阶段和运行阶段是两件不同的事情。配置块仅允许使用提供程序,但在运行块中您可以注入几乎所有内容(提供程序除外)。更多信息在这里 -模块加载和依赖项