如何测试角$ cacheFactory?

时间:2014-10-02 20:22:45

标签: angularjs unit-testing caching

我在角度服务中使用$ resource。 我想使用我自己的基于$ cacheFactory的自定义缓存,而不是内置的$ http缓存,这样我就可以在响应从我的REST服务返回时操作缓存。

我在单元测试中模拟$ cacheFactory很困难,有人可以帮我定义正确的模拟和设置我的测试吗?

以下是我要测试的代码的简化版本。请注意,我告诉我的角度应用使用我的自定义缓存'DefaultCache'而不是$http中可能使用的默认myApp.run()缓存。 (我理解这是替换所有$ resource服务的默认缓存的方法。)

守则

var myServices = angular.module('myServices', ['ngResource']);

var myApp = angular.module('myApp', [
    'myServices'
]);

myApp.run([
    '$http',
    '$cacheFactory',
    function ($http, $cacheFactory) {
        // Replace default $http cache with one we can control
        $http.defaults.cache = $cacheFactory('defaultCache');
    }]);

myServices.factory('MyService', [
    '$resource',
    '$cacheFactory',
    function($resource, $cacheFactory) {

        var cache = $cacheFactory.get('defaultCache');

        function doSomething(data) {
            var cached = cache.get('akey');
            return angular.fromJson(data);
        }

        return {
            things: $resource('/api/things/:id', { id: '@id' }, {
                query: {
                    method: 'GET',
                    isArray: true,
                    cache: true,
                    transformResponse: function(data) {
                        return doSomething(data);
                    },
                },
            })
        }
    }]);

测试

这是我的测试设置。 (注意:提供的mockCacheFactory返回一个对象):

///<reference path="~/Scripts/jasminejs/jasmine.js"/>
///<reference path="../../Web/Scripts/angularjs/angular.js"/>
///<reference path="../../Web/Scripts/angularjs/angular-mocks.js"/>
///<reference path="../../Web/Scripts/angularjs/angular-resource.js"/>

describe("MyServiceSpec", function () {

    beforeEach(module('myServices'));

    describe('GivenThingsResource', function () {

        var $httpBackend, MyService, mockCacheFactory;

        beforeEach(module(function ($provide) {
            mockCacheFactory = {
                get: function (cacheName) {
                    return {
                        get: function (key) { },
                        put: function (key, value) { },
                    };
                },
            };

            $provide.value('$cacheFactory', mockCacheFactory);
        }));

        beforeEach(inject(function (_$httpBackend_, _MyService_) {
            $httpBackend = _$httpBackend_;

            MyService = _MyService_;

            spyOn(mockCacheFactory, 'get').andCallThrough();
        }));

        it("WhenQuery_ThenReturnsData", function () {
            $httpBackend.expectGET('/api/things').respond(JSON.stringify({ Things: [{ Id: '1' }, { Id: '2' }] }));
            var result = MyService.things.query();
            $httpBackend.flush();

            expect(result).toEqualData({ Things: [{ Id: '1' }, { Id: '2' }] });
        });

    });
});

测试结果:

我看到的问题是:当测试运行时,我得到了这两个错误:

TypeError: 'undefined' is not an object (evaluating '$httpBackend.expectGET') in http://localhost:35050/Tests.js (line 76)
TypeError: 'undefined' is not an object (evaluating '$httpBackend.expectGET')
    at  TestSpec.js: line 76
    at   jasmine.js: line 1064
    at   jasmine.js: line 2096
    at   jasmine.js: line 2086TypeError: '[object Object]' is not a function (evaluating '$cacheFactory('$http')')
TypeError: '[object Object]' is not a function (evaluating '$cacheFactory('$http')')
    at   angular.js: line 7527
    at invoke (  angular.js: line 3966)
    at   angular.js: line 3808
    at getService (  angular.js: line 3930)
    at invoke (  angular.js: line 3957)
    at   angular.js: line 3808
    at getService (  angular.js: line 3930)
    at invoke (  angular.js: line 3957)
    at   angular.js: line 3808
    at getService (  angular.js: line 3930)
    at invoke (  angular.js: line 3957)
    at workFn (  angular-mocks.js: line 2161)
    at   jasmine.js: line 1064
    at   jasmine.js: line 2096
    at   jasmine.js: line 2086
undefined

所以,我认为我的模拟是错误的,所以我将提供的mockCacheFactory更改为一个函数,以便对$cacheFactory('$http')的调用成功:

mockCacheFactory = function () {
    return {
        get: function (cacheName) {
            return {
                get: function (key) { },
                put: function (key, value) { },
            };
        },
    };
};

但我现在得到这些错误:

TypeError: 'undefined' is not a function (evaluating '$cacheFactory.get('defaultCache')')
TypeError: 'undefined' is not a function (evaluating '$cacheFactory.get('defaultCache')')
    at  TestSpec.js: line 25
    at invoke (  angular.js: line 3966)
    at   angular.js: line 3808
    at getService (  angular.js: line 3930)
    at invoke (  angular.js: line 3957)
    at workFn (  angular-mocks.js: line 2161)
    at   jasmine.js: line 1064
    at   jasmine.js: line 2096
    at   jasmine.js: line 2086
undefinedTypeError: 'undefined' is not an object (evaluating '$httpBackend.expectGET') in http://localhost:40529/Tests.js (line 78)
TypeError: 'undefined' is not an object (evaluating '$httpBackend.expectGET')
    at  TestSpec.js: line 78
    at   jasmine.js: line 1064
    at   jasmine.js: line 2096
    at   jasmine.js: line 2086

请帮助

我真的不明白我的模拟中需要什么来满足使用我的模拟的代码。它似乎需要有一些javascript二元性,我不知道如何模仿我对mock的定义。

有谁知道我的模拟应该如何构建?或者我在这里还缺少什么?

3 个答案:

答案 0 :(得分:4)

今天我在同样的问题上挣扎,并找到了一个不同的解决方案来完成这项工作。 (Angular 1.3.13)

外观$ cacheFactory有一个get方法,因此您可以检索cacheId的实例。 这将使您能够监视正确的缓存实例。

beforeEach(inject(function (_$cacheFactory_) {
  mockCache = _$cacheFactory_.get('defaultCache');
}));
it('your test', function () {
  spyOn(mockCache, 'get').andCallThrough();
  ....
  expect(mockCache.get).toHaveBeenCalledWith('akey'); 
});

答案 1 :(得分:1)

对于后来遇到同样问题的人。

我发现无法构建适用于此代码的模拟。我甚至采用了$ cacheFactory(来自github)的实现,剥离了它的实现并尝试使用它作为模拟,将其插入测试中。那里也没有运气。

然而,我做了一个随机的帖子,暗示我可能应该创建一个自己的缓存服务(在我自己的服务中),这将使用$ cacheFactory创建一个缓存。然后,我可以将我的cachingservice注入我的资源(正常的服务),而不是直接使用$ cacheFactory。嘲笑缓存服务应该很简单。

我做到了,现在很好。

我的新缓存服务:

myServices.factory('ServiceCachingService', [
    '$cacheFactory',
    function (
        $cacheFactory
        ) {
        return $cacheFactory('defaultCache');
    }]);

新app.run():

myApp.run([
    '$http',
    'ServiceCachingService',
    function ($http, ServiceCachingService) {
        // Replace default $http cache with one we can control
        $http.defaults.cache = ServiceCachingService;
    }]);

我的新数据服务:

myServices.factory('MyService', [
    '$resource',
    'ServiceCachingService',
    function($resource, ServiceCachingService) {

        function doSomething(data) {
            var cached = ServiceCachingService.get('akey');
            return angular.fromJson(data);
        }

        return {
            things: $resource('/api/things/:id', { id: '@id' }, {
                query: {
                    method: 'GET',
                    isArray: true,
                    cache: true,
                    transformResponse: function(data) {
                        return doSomething(data);
                    },
                },
            })
        }
    }]);

然后是新的测试模拟:

mockCachingService = {
    get: function (key) {},
    put: function (key, value) { },
};

答案 2 :(得分:1)

以下代码将允许您在单元测试中模拟$ cacheFactory。 $ provide服务将允许服务依赖注入使用$ cacheFactory而不是默认的$ cacheFactory。

var cache, $cacheFactory; //used in your its
beforeEach(function(){
    module(function ($provide) {
        $cacheFactory = function(){};
        $cacheFactory.get = function(){};

        cache = {
            removeAll: function (){}
            //add other methods here
        };
        spyOn(cache, 'removeAll');
        spyOn($cacheFactory, 'get').and.returnValue(cache);

        $provide.value('$cacheFactory', $cacheFactory);
    });
});

describe('yourFunction', function(){
    it('calls cache.remove()', function(){
        yourService.yourFunction();
        expect(cache.remove).toHaveBeenCalled();
    });
});

https://stackoverflow.com/a/29426640/1270786