使用Jasmine / Karma进行单元测试服务不会注入服务的副本

时间:2016-05-21 15:39:36

标签: angularjs unit-testing jasmine karma-jasmine angular-services

警告,这是针对工作项目的,因此信息有限。如果需要更多信息,请询问,如果我觉得添加我的意愿是合适的。

我花了大约一天半的时间遇到​​了我的单元测试无法加载我正在尝试测试的服务副本的问题。 app.js(包含主应用程序" CC"具有相关部分:

angular
  .module("CC", [
    "ui.router",
    "angularMoment",
    "focus-if",
    "ngCapsLock",
    "CC.AuthInterceptor",
    "CC.Filters",
    "CC.Body",
    "CC.Login",
    "CC.Courtyard",
    "CC.SocketService",
    "CC.HeaderNavbar",
    "CC.footerBar",
    "CC.AcademicHall",
    "CC.ResourceCenter",
    "CC.UserSettings",
    "CC.imageOnLoad",
    "CC.recommendedResources",
    "CC.Constants"
  ])
  .config(ConfigureCookies)
  .config(ConfigureResources)
  .config(ConfigureInterceptors)
  .config(ConfigureDefaultPage)
  .config(ConfigureHTML5Mode)
  .config(ConfigureRouter)
  .run(AuthRedirect);

相关模块(我认为这可能是我的问题)是CC.Constants(由Grunt编写的模块,包含包含相关URL的常量)和CC.SocketService(正在测试的实际服务)。

CC.Constants看起来像这样:

(function () { 
    "use strict";
    angular.module('CC.Constants', [])
        .constant('apiUrl', "<URL1>")
        .constant('socialUrl', "<URL2>"); 
}());

这是我可以从SocketService向您展示的内容,我觉得这一切都是相关的,考虑到我现在还没有尝试明确地测试它,只是进行测试设置。

(function() {
  "use strict";
  function SocketService($rootScope, $cookies, $log, $state, jwtHelper, moment, socialUrl) {
    var socket = {};
    socket.socket = {}; //This holds the socket connection after a specific function is run
    socket.data = {};
    socket.data.sortedMessages = [];
    socket.data.userChannels = [];
    socket.supportFunctions = {};
    socket.supportFunctions.sortPost = function(post) {
      for (var x = socket.data.userChannels.length - 1; x >= 0; x--) {
        if (post.courtyard_post_to === socket.data.userChannels[x].pk) {
          socket.data.userChannels[x].post_to_channel.push(post);
          return;
        }
      }
    };
    /* A bunch of other functions live here all under the socket object in one way or another */
    return socket;
  }
  SocketService.$inject = ["$rootScope", "$cookies", "$log", "$state", "jwtHelper", "moment", "socialUrl"];
  angular
    .module("CC.SocketService", [
      "ngCookies",
      "ui.router",
      "angularMoment",
      "angular-jwt"
    ])
    .factory("SocketService", SocketService);
})();

以下是现在的测试。

(function () {
    "use strict";

    describe("ConnectedCampus.SocketService", function () {
        var $cookies,
            $state,
            Socket;
        var channel1 = {an imitation channel goes here};
        var channel2 = {an imitation channel goes here};
        var channel3 = {an imitation channel goes here};
        var postToChannel1 = {an imitation post goes here};

        beforeEach(function (){
            $cookies = jasmine.createSpyObj("$cookies", ["get", "put"]);
            $cookies.get.and.returnValue("<valid Login Token Goes Here>");
            $state = jasmine.createSpyObj("$state", ["go"]);
            module("CC");
            module("CC.SocketService");
            module(function ($provide) {
                $provide.value("$cookies", $cookies);
                $provide.value("$state", $state);
                $provide.constant("socialUrl", "fakeAddress");
            });
            inject(function (SocketService) {
                Socket = SocketService;
            })
        });
        describe("Support Functions", function () {
            it("socket.supportFunctions.sortPost should push a new post into the channel's posts array.", function (done) {
                console.log("Socket");
                console.log(Socket);//this logs "undefined"
                Socket.data.userChannels = [channel1, channel2, channel3];
                Socket.supportFunctions.sortPost(postToChannel1);
                expect(Socket.data.userChannels[0].post_to_channel).toContain(postToChannel1);
                done();
            })
        });
    });
}());

这是堆栈跟踪:

SocketService@<path to app>/app/socket/socket.js:320:25 //the file is only 208 lines long
invoke@<path to app>/app/libs/angular/angular.js:4535:22
enforcedReturnValue@<path to app>/app/libs/angular/angular.js:4387:43
invoke@<path to app>/app/libs/angular/angular.js:4535:22
<path to app>/app/libs/angular/angular.js:4352:43
getService@<path to app>/app/libs/angular/angular.js:4494:46
invoke@<path to app>/app/libs/angular/angular.js:4526:23
workFn@<path to app>/app/libs/angular-mocks/angular-mocks.js:2517:26
inject@<path to app>/app/libs/angular-mocks/angular-mocks.js:2489:41
<path to app>/app/socket/socket_test.js:321:19
<path to app>/app/login/login_test.js:49:17 //I don't know why these are here, login tests are passing
<path to app>/app/login/login_test.js:43:17 //I don't know why these are here, login tests are passing
<path to app>/app/courtyard/courtyard_test.js:474:21
TypeError: undefined is not an object (evaluating 'Socket.data') in <path to app>/app/socket/socket_test.js (line 337) //Line 337 is the line 'Socket.data.userChannels = [channel1, channel2, channel3];'
/home/anton/git/connected_campus-ng/app/socket/socket_test.js:337:23
<path to app>/app/login/login_test.js:49:17 //I don't know why these are here, login tests are passing
<path to app>/app/login/login_test.js:43:17 //I don't know why these are here, login tests are passing
<path to app>/app/courtyard/courtyard_test.js:474:21 //I don't know why these are here, courtyard tests are passing

这是Karma通过grunt看到的文件:

files: [
    "app/libs/angular/angular.js",
    "app/libs/moment/moment.min.js",
    "app/libs/**/*.js",
    "app/app.js",
    "app/constants.js",
    "app/**/*.js"//SocketService lives in 'app/socket/socket.js'
],

我尝试过的一些事情:

  1. 我尝试过下划线表示法
  2. 我已经尝试过beforeEach在内部描述
  3. 我已经尝试将beforeEach分成多个(每个)(假设)正确的顺序
  4. 我尝试了针对个别测试的内联注入
  5. 我尝试过使用$ injector.get()然后注入每个要求
  6. 我尝试了$ injector.get(&#34; SocketService&#34;)
  7. 以下是依赖关系的版本号(从凉亭和包中提取)。

    BOWER:

    "angular": "1.4.9",
    "angular-cookies": "1.4.9",
    "angular-resource": "1.4.9",
    "angular-animate": "1.4.9",
    "angular-ui-router": "0.2.18",
    "ng-file-upload-shim": "12.0.1",
    "ng-file-upload": "12.0.1",
    "bootstrap":"3.3.6",
    "angular-loader": "1.4.9",
    "angular-mocks": "1.4.9",
    "angular-bootstrap": "1.1.2",
    "fontawesome": "4.5.0",
    "moment": "2.12.0",
    "angular-moment": "1.0.0-beta.5",
    "socket.io-client": "1.4.6",
    "angular-jwt": "0.0.9",
    "ng-focus-if": "1.0.5",
    "ng-caps-lock": "1.0.2",
    "animate.css": "3.5.1",
    "angular-media-queries": "0.5.1"
    

    PACKAGE:

    "bower": "1.7.9",
    "bower-installer": "1.2.0",
    "grunt": "1.0.1",
    "grunt-contrib-concat": "1.0.1",
    "grunt-contrib-cssmin": "1.0.1",
    "grunt-contrib-htmlmin": "1.4.0",
    "grunt-contrib-jshint": "1.0.0",
    "grunt-contrib-sass": "1.0.0",
    "grunt-contrib-watch": "1.0.0",
    "grunt-githooks": "0.6.0",
    "grunt-karma": "1.0.0",
    "grunt-ng-annotate": "2.0.2",
    "grunt-ng-constant": "2.0.1",
    "http-server": "0.9.0",
    "jasmine-core": "2.4.1",
    "jshint-stylish": "2.2.0",
    "karma": "0.13.22",
    "karma-coverage": "1.0.0",
    "karma-ie-launcher": "1.0.0",
    "karma-jasmine": "1.0.2",
    "karma-junit-reporter": "1.0.0",
    "karma-phantomjs-launcher": "1.0.0",
    "karma-safari-launcher": "1.0.0",
    "phantomjs-prebuilt": "2.1.7",
    "protractor": "3.3.0",
    "shelljs": "0.7.0"
    

    绝对不会得到任何帮助。我不明白这一点,我之前测试过的服务,以及实际的Socket代码是否有效。同样,如果您需要更多信息,请随时提出,但我可能无法提供给您。

1 个答案:

答案 0 :(得分:1)

我发现了这个问题,它不在我透露的范围之内。我不确定为什么我的更改有效,但在我的情况下确实如此,如果有人遇到同样的问题,我想把我的修改放在那里。

SocketService是一个服务,它将Socket.IO套接字包装在个人逻辑中,并将javascript全局io.connect()和其他io / socket功能带入角度生态系统。我有它,以便在调用事件socket.createConnection()时创建实际连接,该事件在实际代码中起作用,但由于某种原因不能在测试中工作。我使连接成为每次初始化文件顶部附近的socket.socket = io.connect(socialUrl)服务时触发的事件,这似乎解决了这个问题。所以现在文件的顶部看起来像:

function SocketService($rootScope, $cookies, $log, $state, jwtHelper, moment, socialUrl) {
    /* The object to be returned by the service, includes:
     * .data(actual data),
     * .supportFunctions(functions to sort and place data),
     * .callbackFunctions(callbacks to call on 'on' events
     * .socket which contains the actual socket connection */
    var socket = {};
    /* Object to hold socket connection */
    socket.socket = io.connect(socialUrl); //This is the important change
    /* Holds all the data gathered so far by the socket */
    socket.data = {};
    /* Holds all the sorted messages */
    socket.data.sortedMessages = [];
    /* Holds all the channels the user is a member of */
    socket.data.userChannels = [];
    /* Object holding all the support functions necessary for the data to be manipulated as expected */
    socket.supportFunctions = {};

现在使用包含下划线标注声明的外部描述。如下所示,此测试现在通过意味着更改有效:

(function () {
    "use strict";

    describe("ConnectedCampus.SocketService", function () {
        /* jshint unused: false */
        var $rootScope,
            $cookies,
            $log,
            $state,
            jwtHelper,
            moment,
            SocketService;
        var channel1 = <imitation channel object>;
        var channel2 = <imitation channel object>;
        var channel3 = <imitation channel object>;
        var postToChannel1 = <imitation post to 1st channel>;

        beforeEach(function (){
            $cookies = jasmine.createSpyObj("$cookies", ["get", "put"]);
            $cookies.get.and.returnValue("<valid auth token>");
            $state = jasmine.createSpyObj("$state", ["go"]);
            module("angularMoment");
            module("angular-jwt");
            module("ConnectedCampus.Constants");
            module("ConnectedCampus.SocketService");
            module(function ($provide) {
                $provide.value("$cookies", $cookies);
                $provide.value("$state", $state);
                $provide.constant("socialUrl", "Yellow");
            });
            inject(function (_$rootScope_, _$log_, _jwtHelper_, _moment_, _SocketService_) {
                $rootScope = _$rootScope_.$new();
                $log = _$log_;
                jwtHelper = _jwtHelper_;
                moment = _moment_;
                SocketService = _SocketService_;
            })
        });

        describe("Support Functions", function () {


            it("socket.supportFunctions.sortPost should push a new post into the channel's posts array.", function (done) {
                SocketService.data.userChannels = [channel1, channel2, channel3];
                SocketService.supportFunctions.sortPost(postToChannel1);
                expect(SocketService.data.userChannels[0].post_to_channel).toContain(postToChannel1);
                done();
            })
        });
    });
}());

同样,我不确定为什么这有效,而且我不想提出意见,但如果你遇到过类似的问题,这个可能成为解决方案。