Как издеваться над обещаниями AngularJS с помощью Jasmine/Karma?

Это мой контроллер:

angular.module("AuthenticationApp", ["BaseApp"])
    .controller("AuthCtrl", ["$http", "BaseService", function($http, BaseService) {
        var self = this;

        BaseService.fetch.stuffs()
          .then(function(data) {
            self.stuffs = data;
        });

    }]);

Это мой BaseService (фабрика, которую мне нужно издеваться):

angular.module("BaseApp", ["ngRoute"])

    .factory("BaseService", ["$http", "$q", function($http, $q) {
        var self = this;

        self.fetch = {
         stuffs: function() {
           return $http.get("/api/stuffs/")
             .then(function(response) {
                 return response.data;
           });
         }
        };    

        return self;
    }]);

Я понятия не имею, с чего начать. Я пробовал следующее:

describe('Controller: AuthCtrl', function() {
    var ctrl, mockBaseService;

    beforeEach(function() {

        mockBaseService = {
            stuffs: 'test',
            fetch: { 
                stuffs: function() {
                    return {
                      then: function() {
                          return {stuffs: "stuffs"};
                      },
                    }
                },
            },
        };

        spyOn(mockBaseService.fetch, 'stuffs');

        module('BaseApp', function($provide) {
            $provide.value('BaseService', mockBaseService);
        });

        module('AuthenticationApp');

        inject(function($controller) {
            ctrl = $controller('AuthCtrl', {
            });
        });
    });

    it('BaseService.fetch.stuffs should be called on page load', function() {
        expect(mockBaseService.fetch.stuffs).toHaveBeenCalled();
        expect(ctrl.stuffs).toBe({stuffs: "stuffs"});
    });

});

Но когда я тестирую этот код, я получаю сообщение об ошибке:

TypeError: Cannot read property 'then' of undefined
    at new <anonymous> (/home/user/Documents/ebdjango/ebdjangoapp/static/js/home.js:15:11)

home.js строка 15 это строка в контроллере:

       return $http.get("/api/stuffs/")
         .then(function(response) { // line 15
             return response.data;
       });

Как правильно издеваться над этим?

Изменить: я прочитал документацию, упомянутую в комментариях, и изменил mockBaseService на это:

describe('Controller: AuthCtrl', function() {
    var ctrl, mockBaseService;

    beforeEach(function() {
        // create promise
        var deferred = $q.defer();
        var promise = deferred.promise;
        // make promise.then return {stuffs: "stuffs"}
        promise.then(function(value) { return {stuffs: "stuffs" };  });

        mockBaseService = {
            stuffs: 'stuffs',
            fetch: { 
                stuffs: function() {
                    return promise;
                },
            },
        };

        spyOn(mockBaseService.fetch, 'stuffs');

        module('BaseApp', function($provide) {
            $provide.value('BaseService', mockBaseService);
        });

        module('AuthenticationApp');

        inject(function($controller) {
            ctrl = $controller('AuthCtrl', {
            });
        });
    });

    it('BaseService.fetch.stuffs should be called on page load', inject(function($rootScope) {
        deferred.resolve('test');
        $rootScope.$apply();
        expect(mockBaseService.fetch.stuffs).toHaveBeenCalled();
        expect(ctrl.stuffs).toBe({stuffs: "stuffs"});
    }));

});

Но это возвращает ошибку ReferenceError: $q is not defined. Я попытался ввести _$q_ вот так:

beforeEach(inject(function(_$q_) {
    $q = _$q_;
    // ... everything else goes here... //

Но потом я получаю сообщение об ошибке Error: Injector already created, can not register a module!. Здесь: инжектор уже создан. не могу зарегистрировать модуль пишет, что эту проблему можно решить, если мы выполним module('someApp') до inject($someDependency). Но я не могу

module('BaseApp', function($provide) {
    $provide.value('BaseService', mockBaseService);
});

перед вводом _$q_, так как $q используется для mockBaseService. Куда мне идти отсюда?


person SilentDev    schedule 09.04.2017    source источник
comment
@georgeawg Спасибо. Я прочитал эти ссылки и попытался реализовать то, что узнал. Можете ли вы проверить, на правильном ли я пути, и как решить новую ошибку, которую я получаю?   -  person SilentDev    schedule 10.04.2017


Ответы (1)


издеваться над обещаниями я всегда делаю

1) spyOn(service,'method').andCallFake( $q.when( Fixture.data() ) )

Не создавайте этот mockBaseService, я делал это раньше, и это много писать впустую. Также можно использовать AngularJS, встроенный в способ перехвата $http, т.е.

2) $httpBackend.when('GET', '/url').respond(Fixture.data())

Я определенно предпочитаю 1), так как мне нравится изолировать себя в определенном слое при тестировании. Это сводится к вопросу вкуса, я думаю

person Chris Noring    schedule 09.04.2017
comment
Хорошо спасибо. Кстати, что такое Fixture и Fixture.data() в примере 1? - person SilentDev; 10.04.2017
comment
О, это просто мое личное дело... Это данные, которыми вы отвечаете. Хорошей практикой является сохранение всех данных ответа в Fixture следующим образом: function Fixture(){ this.user ={ name : 'Adam' }; this.accounts = [ { баланс : 100 } ] } - person Chris Noring; 10.04.2017
comment
Ох, ладно. Итак, я попытался сделать то, что вы упомянули (т.е. добавить следующее внутри beforeEach: function Fixture() { ... }, а затем передать Fixture.data() в and.callFake($q.when(Fixture.data()))) но я получаю сообщение об ошибке, говорящее, что Fixture.data не является функцией. Есть идеи, как это решить? - person SilentDev; 10.04.2017
comment
вот jsbin с тремя вариантами создания приспособления: jsbin.com/ xepaxaw/edit?html,js,консоль,вывод - person Chris Noring; 11.04.2017
comment
Я получаю сообщение об ошибке Argument passed to callFake should be a function, got [object Object] Когда я делаю and.callFake( $q.when( Fixture.data()) ). Я использую первый вариант создания фикстуры, но и второй выдает ту же ошибку. Изменить: на основе ответа здесь: stackoverflow.com/questions /24997174/ может быть, это связано с тем, что $q.when становится функцией, а не значением, которое передается в and.callFake()? Я не знаю, как решить эту проблему. - person SilentDev; 13.04.2017
comment
вы правы, это должно быть and.returnValue($q.when(data)) или and.callFake(function(){ return { then : function(resolve, reject){ resolve(10) } } } ) @user2719875 - person Chris Noring; 13.04.2017