AngularJS/Karma/Jasmine - Сервисный звонок не возвращает значение

Я пытаюсь вызвать API-интерфейс Github, используя сервис, внедренный в компонент - и да, я использую AngularJS 1.5.3.

В модульном тесте я не получаю обратно значение (функция работает, когда я запускаю ее в браузере). Я не уверен, что я делаю неправильно, и надеюсь, что кто-то может указать мне правильное направление.

Вот ошибка: введите описание изображения здесь

main.component.js

(function(){
    angular.module("app").component("mainComponent", {
        templateUrl: "/templates/main.component.html",
        controllerAs: "vm",
        controller: function(APIFactory, UserFactory, $state){
            const vm = this;

            vm.searchGithub = function(){
                APIFactory.getAPI(vm.searchText).then(function(res){
                    res.status !== 200 ? $state.go("404", {errorData: res.data }) : (
                        vm.User = new UserFactory.User(res.data),
                        $state.go("profile", {userData: vm.User})
                    );
                })
                .catch(function(err){
                    $state.go("fourOFour");
                });
            };
        }
    });
})();

main.component.spec.js

describe("Main Component", function(){
    var mainComponent, APIFactory, UserFactory, $httpBackend, $q, $state, $rootScope;

    const addy = "https://api.github.com/users/";

    beforeEach(angular.mock.module("app"));

    beforeEach(inject(function(_APIFactory_, _UserFactory_, _$httpBackend_, _$state_, _$q_, _$rootScope_, _$componentController_){
        APIFactory = _APIFactory_;
        UserFactory = _UserFactory_;
        $httpBackend = _$httpBackend_;
        $state = _$state_;
        $q = _$q_;
        $rootScope = _$rootScope_;
        $rootScope.$new();
        mainComponent = _$componentController_("mainComponent", { $scope : {} });
    }));

    describe("Checking if the searchGithub() worked correctly", function(){
        var result;

        beforeEach(function(){
            spyOn(mainComponent, "searchGithub").and.callThrough();
            spyOn(APIFactory, "getAPI").and.callThrough();
            result = {};
        });

        it("should make a call to UserFactory", function(){
            mainComponent.searchText = "someName";
            expect(mainComponent.searchText).toBeDefined();

            // RESPONSE_SUCCESS does exist, I've omitted it.
            $httpBackend.whenGET(addy + mainComponent.searchText).respond(200, $q.when(RESPONSE_SUCCESS));

            // This is where I expect something to work

            APIFactory.getAPI(mainComponent.searchText).then(function(res){
                result = res;
            });

            $httpBackend.flush();

            expect(APIFactory.getAPI).toHaveBeenCalledWith(mainComponent.searchText);
            expect(mainComponent.User).toBeDefined();
        });
    });


});

2 ответа

В ответе выше вы вручную звоните UserFactoryMock.User в тестовом примере, который создаст объект пользователя.

Но чтобы правильно проверить функционал, нужно проверять UserFactory.User быть вызванным при вызове APIFactory.getAPI это успех (без звонка UserFactory.User вручную в тесте.

Я бы предложил изменить ваш тестовый пример на что-то вроде ниже:

describe("Main Component", function(){
var mainComponent, APIFactory, UserFactory, $httpBackend, $q, $state, $rootScope;

const addy = "https://api.github.com/users/";

beforeEach(angular.mock.module("app"));

beforeEach(inject(function(_APIFactory_, _UserFactory_, _$httpBackend_, _$state_, _$q_, _$rootScope_, _$componentController_){
    APIFactory = _APIFactory_;
    UserFactory = _UserFactory_;
    $httpBackend = _$httpBackend_;
    $state = _$state_;
    $q = _$q_;
    $rootScope = _$rootScope_;
    var scope = $rootScope.$new();
    var bindings = { APIFactory: APIFactory, UserFactory: UserFactory, $state: $state };
    mainComponent = _$componentController_("mainComponent", { $scope : scope }, bindings);
}));

describe("Checking if the searchGithub() worked correctly", function(){
    var result;

    beforeEach(function(){
        spyOn(mainComponent, "searchGithub").and.callThrough();
        spyOn(APIFactory, "getAPI").and.callFake(function() {
            var def = $q.defer();
            def.resolve(RESPONSE_SUCCESS);
            return def.promise;
        });
        spyOn(UserFactory, "User").and.callFake(function() {
            var user = { id: 666, .... };
            return user;
        });
    });

    it("should make a call to UserFactory", function(){
        mainComponent.searchText = "someName";
        $rootScope.$apply();
        expect(mainComponent.searchText).toBeDefined();

        mainComponent.searchGithub(); // Call the same way as it works in the code actually.

        $rootScope.$apply();

        //No manual call to 'UserFactory.User' or 'APIFactory.getAPI'. The call to 'APIFactory.getAPI' is resolved/succeeds, hence a call to 'UserFactory.User' is made and the same is tested
        expect(APIFactory.getAPI).toHaveBeenCalledWith(mainComponent.searchText);
        expect(UserFactory.User).toHaveBeenCalledWith(RESPONSE_SUCCESS.data);
        expect(mainComponent.User).toBeDefined();
        expect(mainComponent.User.id).toEqual(666);
    });
});


});

Так что это то, что я придумал для решения. Если кто-то хочет дать мне лучшее решение, я готов к идеям.

Сначала я сделал две насмешки, а затем ввел их в mainComponentШпион за моим издевательством APIFactoryMock.getAPI функция:

const APIFactoryMock = {
    getAPI: function(){}
};

const UserFactoryMock = {
    User: function(data){
        return {
            login: data.login,
            id: data.id,
            avatar_url: data.avatar_url,
            html_url: data.html_url,
            followers: data.followers,
            following: data.following,
            public_repos: data.public_repos,
            public_gists: data.public_gists,
            created_at: data.created_at,
            updated_at: data.updated_at,
            name: data.name,
            company: data.company,
            blog: data.blog,
            location: data.location,
            bio: data.bio,
            hireable: data.hireable,
            email: data.email,
            links: {
                followers_url: data.followers_url,
                following_url: data.following_url,
                subscriptions_url: data.subscriptions_url,
                repos_url: data.repos_url,
                organizations_url: data.organizations_url
            }
        }
    }
};

beforeEach(inject(function(_APIFactory_, _UserFactory_, _$httpBackend_, _$state_, _$q_, _$rootScope_, _$componentController_){
    APIFactory = _APIFactory_;
    UserFactory = _UserFactory_;
    $httpBackend = _$httpBackend_;
    $state = _$state_;
    $q = _$q_;
    $rootScope = _$rootScope_;
    $rootScope.$new();
    spyOn(APIFactoryMock, "getAPI").and.returnValue(RESPONSE_SUCCESS);
    bindings = { APIFactory: APIFactoryMock, UserFactory: UserFactoryMock, $state: $state };
    mainComponent = _$componentController_("mainComponent", { $scope : {} }, bindings);
}));

А потом я написал тесты на макеты:

it("should make a call to UserFactory", function(){
        mainComponent.searchText = "someName";
        expect(mainComponent.searchText).toBeDefined();


        mainComponent.searchGithub(mainComponent.searchText);
        $httpBackend.whenGET(addy + mainComponent.searchText).respond(200, $q.when(RESPONSE_SUCCESS));

        $httpBackend.flush();

        mainComponent.User = UserFactoryMock.User(RESPONSE_SUCCESS.data);

        expect(mainComponent.searchGithub).toHaveBeenCalledWith(mainComponent.searchText);
        expect(mainComponent.User).toBeDefined();
        expect(mainComponent.User.id).toEqual(666);
    });
Другие вопросы по тегам