尝试测试通用 angularjs $resource 服务时出现未知提供程序错误

Unknown Provider Error while trying to test generic angularjs $resource service

我有为我的应用程序创建资源的通用服务:

(function(module) {
module.provider('restService', {

    resourceRegistry: {},
    addRestResource: function(entityName, entityProto) {
        this.resourceRegistry[entityName] = entityProto;
    },

    $get: function() {
        var restService;
        for (var entityName in this.resourceRegistry) {
            createRestResource(entityName, this.resourceRegistry[entityName]);
        };

        restService = {
            //createRestResource: createRestResource
        };

        return restService;
  }});

  function createRestResource(entityName, entityProto) {
    console.log('registering model: ' + entityName);
    module.provider(entityName, { $get: function($resource, $http) {
        var resource = $resource('/api/' + entityName + '/:id', { // TODO use config
            id : '@id' //this binds the ID of the model to the URL param
        },{
            query : { method : 'GET', isArray : true }, //this can also be called index or all
            update : { method : 'PUT' },
            create : { method : 'POST' },
            destroy : { method : 'DELETE' }
        });

        // here gose some other functionality unrelated to the topic...

        return resource;
    }});
}}(angular.module('restService', ['ngResource'])));

我可以被任何其他模块使用

module.config(['restServiceProvider', function(restServiceProvider) {
  restServiceProvider.addRestResource('User', { name: null, email: null });
}

虽然上面在应用程序中对我有效实际上上面对我在应用程序中都不起作用(由于重构之前遗留的一些代码它正在工作)我也不能开始工作 jasmine/karma 测试它。问题是尝试各种方法来配置 restServiceProvider 我总是以错误结束,指出例如 TestEntity 有未知的提供者 TestEntityProider。虽然我在创建资源之前尝试了不同的方法来配置 resourceRegistry,但这里有一些测试文件。

describe('testing restService', function () {
    var httpBackend;
    var theRestServiceProvider;

    beforeEach(function() {
        module('ngResource');
        module('restService');
        var fakeModule = angular.module('test.app.config', ['ngResource'], function () {});
        fakeModule.config( function (restServiceProvider) {
            theRestServiceProvider = restServiceProvider;
            restServiceProvider.addRestResource('TestModel', {testProp: null});
        });

        module('test.app.config');
    });

    beforeEach(function () {
        inject(function ($httpBackend) {
            httpBackend = $httpBackend;
        })
    });

    beforeEach(inject(function (restService) {}));



    describe('create restService entity', function() {
        it('should post entity with nonempty testProp',
            function() {
                theRestServiceProvider.addRestResource('TestModel', {testProp: null});

                inject(function(TestModel) {
                    var object = new TestModel();
                    object.testProp = 'John Doe';

                    httpBackend.expectPOST(/.*/,
                        function(postData) {
                            console.log("post data: " + postData);
                            jsonData = JSON.parse(postData);
                            expect(jsonData.testProp).toBe(object.testProp);

                            return jsonData;
                        }).respond({});

                    var response = object.$create();

                    httpBackend.flush();
                });
            });
    });
});

IMO 这是因为在测试中正在注册资源 'too late' 但我仍然不知道如何正确执行此操作。

编辑这里是最终的解决快捷方式:

(function(module) {
  module.provider('restService', function($provide) {
    var provider = {};
    // all the provider stuff goes here

    function createRestResource(entityName, entityProto) {
      /* 
       * using $provider here is fundamental here to properly create 
       * 'entityName' + Provider in the runtime instead of module initialisation
       * block
       */
       $provide.factory(entityName, function($resource, $http) { /* ... */ };
       // do other stuff...
    }

    return provider;
  }
}(angular.module('restServiceModule', ['ngResource'])))

我对karma/jasmine不是很熟悉,但BDD语法似乎是相似的。 根据我的理解,你应该有这样的东西

beforeEach(function () {
    module('restService');
    inject(function (_$httpBackend_, _restService_, _ngResource_) {
        $httpBackend = _$httpBackend_;
        restService = _restService_;
        ngResource = _ngResource_;
    })
});

而不是你们所有人beforeEach。阅读更多 here。 如果您想提供模拟而不是注入服务,您可以使用

    module('restService', function($provide){
        $provide.value('serviceToMock', mockObject);
    });

请注意,命名模块和 provider/service/factory 同名可能会在后期混淆...

我在这里做了非常相似的东西https://github.com/tunguski/matsuo-ng-resource/blob/master/matsuo-ng-resource.js,也许对你有帮助。

基本上我将新的资源提供者添加到 $provide 而不是 module。有用。我认为这是主要区别。