如何用 Angular 和 Jasmine 模拟 socket.io
How to mock socket.io with Angular and Jasmine
我无法弄清楚如何使用 Jasmine 和 Karma 在 Angular 应用程序中正确模拟 Socket.io。
以下是在 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'
这是我的控制器的样子:
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})
});
}]);
这是我的套接字工厂:
var lunchrFactories = angular.module('lunchrFactories', []);
lunchrFactories.factory('socket', function (socketFactory) {
return socketFactory();
});
这是主要的 angular 模块:
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);
}]);
这是我无法理解的测试:
'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
})
})
});
我怀疑 socket
没有被正确注入,我不确定如何修复它。同样,我也希望能够调用 socket.emit
但不知道如何调用。
如有任何帮助,我们将不胜感激。
编辑:如果可能的话,我想使用 this socket mock,但我无法弄清楚它到底是如何工作的。
我会使用 Sinon.js 创建模拟套接字对象。
例如,在你的beforeEach
中,将socket初始化为一个空对象:
socket = {};
createController()
必须将 mock 传递给控制器:
$controller('UserMatchingController', {'$scope': $rootScope, 'socket': socket});
然后在你的测试中做这样的事情:
socket.emit = sinon.spy(); //Create a spy so we can check if it was called
createController();
expect(socket.emit).toHaveBeenCalledWith('match');
您将需要软件包 sinon and jasmine-sinon. Also if you are using Karma, it will need karma-sinon。您可以在相应页面上找到有关设置的更多指南。
编辑:在您的情况下,您的模拟每次都需要 on
和 emit
函数,因为控制器需要它们。在这种情况下,将其放入 beforeEach
函数中可能会更好:
socket = {
emit : sinon.spy(),
on : sinon.spy()
};
我最终使用了 this solution 中定义的 sockMock
对象。
我无法弄清楚如何使用 Jasmine 和 Karma 在 Angular 应用程序中正确模拟 Socket.io。
以下是在 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'
这是我的控制器的样子:
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})
});
}]);
这是我的套接字工厂:
var lunchrFactories = angular.module('lunchrFactories', []);
lunchrFactories.factory('socket', function (socketFactory) {
return socketFactory();
});
这是主要的 angular 模块:
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);
}]);
这是我无法理解的测试:
'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
})
})
});
我怀疑 socket
没有被正确注入,我不确定如何修复它。同样,我也希望能够调用 socket.emit
但不知道如何调用。
如有任何帮助,我们将不胜感激。
编辑:如果可能的话,我想使用 this socket mock,但我无法弄清楚它到底是如何工作的。
我会使用 Sinon.js 创建模拟套接字对象。
例如,在你的beforeEach
中,将socket初始化为一个空对象:
socket = {};
createController()
必须将 mock 传递给控制器:
$controller('UserMatchingController', {'$scope': $rootScope, 'socket': socket});
然后在你的测试中做这样的事情:
socket.emit = sinon.spy(); //Create a spy so we can check if it was called
createController();
expect(socket.emit).toHaveBeenCalledWith('match');
您将需要软件包 sinon and jasmine-sinon. Also if you are using Karma, it will need karma-sinon。您可以在相应页面上找到有关设置的更多指南。
编辑:在您的情况下,您的模拟每次都需要 on
和 emit
函数,因为控制器需要它们。在这种情况下,将其放入 beforeEach
函数中可能会更好:
socket = {
emit : sinon.spy(),
on : sinon.spy()
};
我最终使用了 this solution 中定义的 sockMock
对象。