背景 我正在为angular js控制器编写单元测试,它利用服务中包含的角度$资源(用于可维护性目的)。
示例控制器:
name = 'app.controllers.UsersIndexCtrl'
angular.module(name, [])
.controller(name, [
'$scope'
'$location'
'$dialog'
'Users'
'UserRoles'
($scope, $location, $dialog, Users, UserRoles) ->
# Get users list
$scope.users = Users.query()
# rest...
])
资源服务示例:
angular.module('app.services.Users', [])
.factory 'Users', ['$rootScope', '$http', '$resource', '$location' , ($rootScope, $http, $resource, $location)->
baseUrl = '/users'
Users = $resource baseUrl + '/:userId', {userId: '@_id'}
Users.getStatus = ->
console.log 'User::getStatus()'
req = $http.get baseUrl + '/status'
req.success (res)->
$rootScope.globalUserAccountSettings = res
unless $rootScope.$$phase then $rootScope.$apply()
# other, custom methods go here...
])
大多数角度建议的单元测试示例使用$ httpBackend,因此在控制器中模拟$ http服务。说实话,我怀疑这是否是一个好习惯,因为如果这样做我必须在所有控制器测试中硬编码请求路径,我想隔离单元行为。 $ httpBackend mock非常棒,但前提是你直接在控制器中使用$ resource。
使用$ httpBackend的典型单一测试如下所示:
it 'should be able to add a new empty user profile', ->
$httpBackend.flush()
l = $scope.users.length
$httpBackend.expect('POST', '/users').respond _.cloneDeep mockResponseOK[0]
$scope.add()
$httpBackend.flush()
expect($scope.users.length).toEqual l + 1
如果我创建了一个模拟用户资源类实例,例如:
,该怎么办?angular.module('app.services.Users', [])
.factory 'Users', ->
class Users
$save:(cb)->
$remove:->
@query:->
@get:->
Angular DI机制将以透明的方式覆盖旧的'app.services.Users'模块,并允许我使用茉莉花间谍运行检查。
困扰我的是我无法找到支持我的想法的单个示例。所以问题是,你会使用哪一个,为什么或我做错了什么?
答案 0 :(得分:4)
我认为如你所说,使用Jasmine间谍在服务级别存储它更有意义。此时您正在对控制器进行单元测试,而不是服务 - http请求的确切方式不应该是此测试的关注点。
您可以在您的规范中执行以下操作:
var Users = jasmine.createSpyObj('UsersStub', ['query', 'get']);
beforeEach(inject(function($provide) {
$provide.factory('Users', function(){
return Users;
});
});
然后在您的相关测试中,您可以使用间谍对象上的“andCallFake”等方法来存储各个服务方法以返回您期望的内容。
答案 1 :(得分:1)
你能做的最好的事情是使用假设要调用的方法制作假资源:
var queryResponse = ['mary', 'joseph'],
Users = function() {
this.query = function() {
return queryResponse;
},
scope, HomeCtrl;
};
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
HomeCtrl = $controller('HomeCtrl', {$scope: scope, Users: new Users()});
}));
it('has users in scope', function() {
expect(scope.users).toEqual(queryResponse);
});
答案 2 :(得分:0)
我是这个东西的新手。我一直在用clfeescript使用dsl编写我的测试,但今天我遇到了类似的问题。我解决它的方法是为我的资源创建一个茉莉花间谍。然后我创造了一个承诺。当promise被解决时,它将调用您在控制器中传入的“success”函数。然后在'it'方法中,我实际上解决了这个承诺。
我认为代码看起来像使用js和jasmine,但我实际上没有时间检查
beforeEach(inject(function($rootScope, $controller, $q ) {
scope = $rootScope.$new();
queryPromise = $q.defer()
User = jasmine.createSpyObject("UsersStub", ["query"]);
User.query.andCallFake(function(success,errror){queryPromise.promise.then(success,error)});
HomeCtrl = $controller('HomeCtrl', {$scope: scope, Users: new Users()});
}));
it('has users in scope', function() {
queryPrmomise.resolve({User1: {name"joe"})
expect(scope.users).toEqual(queryResponse);
});