AngularJS / Karma / Jasmine - 服务电话没有返回值

时间:2017-03-31 17:00:35

标签: javascript angularjs unit-testing karma-jasmine angularjs-1.6

我正在尝试使用注入组件的服务来调用Github API - 是的,我使用的是AngularJS 1.5.3。

在单元测试中,我没有收到一个值(当我在浏览器中运行时,该功能会起作用)。我不确定我做错了什么,希望有人能指出我正确的方向。

这是错误: enter image description here

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 个答案:

答案 0 :(得分:0)

所以这就是我提出的解决方案。如果有人想给我一个更好的解决方案,我就是想要的。

首先,我制作了两个嘲讽,然后将它们注入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);
    });

答案 1 :(得分:0)

在上面的答案中,您手动调用测试用例中的UserFactoryMock.User,这将创建一个用户对象。

但要正确测试功能,应该在调用UserFactory.User成功时检查是否要调用APIFactory.getAPI(不在测试用例中手动调用UserFactory.User。< / p>

我建议将测试用例修改为以下内容:

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


});