How to mock socket.io with Angular and Jasmine

北城以北 提交于 2019-12-10 10:50:31

问题


I'm having trouble figuring out how to correctly mock Socket.io in an Angular application using Jasmine and Karma.

Here are the files found in karma.conf.js:

'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'bower_components/angular-ui-router/release/angular-ui-router.js',
'bower_components/angular-socket-io/socket.js',
'bower_components/socket.io-client/socket.io.js',
'bower_components/angular-socket-io/mock/socket-io.js',
'public/javascripts/*.js',
'ng_tests/**/*.js',
'ng_tests/stateMock.js'

Here is how my controller looks:

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

lunchrControllers.controller('UserMatchingController', ['$state', 'socket',
    function ($state, socket) {
        socket.emit('match');

        socket.on('matched', function (data) {
            $state.go('users.matched', {name: data.name})
        });
    }]);

Here is my socket factory:

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

lunchrFactories.factory('socket', function (socketFactory) {
    return socketFactory();
});

Here is the main angular module:

var lunchrApp = angular.module('lunchr', ['ui.router', 'btford.socket-io', 'lunchrControllers', 'lunchrFactories' ]);

lunchrApp.config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
    function ($stateProvider, $urlRouterProvider, $locationProvider) {
        $urlRouterProvider.otherwise('/');

        $stateProvider.
            state('mainPage', {
                url: '/',
                templateUrl: '/partials/main.jade',
                controller: 'MainPageController'
            })
            .state('users', {
                url: '/users',
                templateUrl: '/partials/users.jade',
                controller: 'UserController'
            })
            .state('users.matching', {
                url: '/matching',
                templateUrl: '/partials/users.matching.jade',
                controller: 'UserMatchingController'
            })
            .state('users.matched', {
                url: '/matched',
                templateUrl: '/partials/users.matched.jade',
                params:{name:null},
                controller: 'UserMatchedController'
            })
            .state('register', {
                url:'/register',
                templateUrl: 'partials/register.jade',
                controller: 'RegisterController'
            });
        $locationProvider.html5Mode(true);
    }]);

Here is the test I'm having trouble figuring out:

'use strict';

describe('UserMatchingController', function () {
    beforeEach(module('lunchr'));
    beforeEach(module('stateMock'));


    var $controller, $rootScope, $state, socket;

    beforeEach(inject(function ($injector) {
        // Get hold of a scope (i.e. the root scope)
        $rootScope = $injector.get('$rootScope');

        $state = $injector.get('$state');

        // The $controller service is used to create instances of controllers
        $controller = $injector.get('$controller');

        socket = $injector('socket');
    }));

    afterEach(function () {
        $state.ensureAllTransitionsHappened();
    });

    function createController() {
        return $controller('UserMatchingController', {'$scope': $rootScope});
    }

    describe('on initiazation', function(){
        it('socket should emit a match', function() {
            createController();

            // would like to be able to expect a socket.emit call
        });
        it('should transition to users.matched upon receiving matched', function(){
            createController();
            $state.expectTransitionTo('user.matched');
            socket.receive('matched', {name: 'Ben'}); //This errors out with: cannot read property 'receive' of undefined
        })
    })
});

I suspect that socket isn't being properly injected and I'm not sure how to fix it. Similarly, I'd also like to be able to expect that a call to socket.emit is made but don't know how.

Any help would be appreciated.

EDIT: I'd like to use this socket mock if possible but I'm having trouble figuring out exactly how it works.


回答1:


I ended up using the sockMock object defined in this solution.




回答2:


I would use Sinon.js to create a mock socket object.

For example, in your beforeEach, initialize socket to be an empty object:

socket = {};

createController() must pass the mock to the controller:

$controller('UserMatchingController', {'$scope': $rootScope, 'socket': socket});

Then in your test do something like this:

socket.emit = sinon.spy(); //Create a spy so we can check if it was called
createController();

expect(socket.emit).toHaveBeenCalledWith('match');

You will need the packages sinon and jasmine-sinon. Also if you are using Karma, it will need karma-sinon. You can find more guidelines on the setup on the respective pages.

EDIT: In your case your mock needs to have on and emit functions every time, as they are needed by the controller. In this case it might be better to put this into the beforeEach function:

socket = {
  emit : sinon.spy(),
  on : sinon.spy()
};


来源:https://stackoverflow.com/questions/28846048/how-to-mock-socket-io-with-angular-and-jasmine

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!