我正在测试我的AngularJS应用程序,我正在努力进行注入服务的控制器的测试。
这是我的控制器:
.controller('UserCtrl', ['$scope', '$routeParams', 'User', function ($scope, $routeParams, User) {
$scope.hello = "lol";
$scope.userId = $routeParams.id;
var user = new User($scope.userId);
$scope.load = function(){
user.load().then(
function(user){
$scope.name = user.name;
},
function() {
$scope.dataFail = 'error error' ;
}
);
};
$scope.load();
}]);
这是我的用户服务(我想在我的控制器测试中注入)
.factory('user', ['$http', '$q', function ($http, $q) {
var User = function (id) {
this.id = id;
this.name = null;
};
User.prototype.load = function () {
var deferred = $q.defer();
$http.post('/user/' + this.id + '/load')
.success(function(data) {
var user = new User(data.id);
user.name = data.name
deferred.resolve(user);
})
.error(function(err, code) {
deferred.reject(err);
$log.error(err, code);
});
return deferred.promise;
};
return User;
最后是我的考试:
describe('Controller: UserCtrl', function () {
var $controller, $scope, User;
beforeEach(module('octosendApp', function($provide){
User = jasmine.createSpyObj('User', ['load']);
User.load.and.returnValue({
name: 'testName'
});
$provide.value('User', User);
}));
beforeEach(inject(function(_$controller_, $rootScope, _User_){
$scope = $rootScope.$new();
User = _User_;
$controller = _$controller_('UserCtrl', {
$scope: $scope,
User : User,
$routeParams: {id: 2}
});
}));
it('should have a hello property', function() {
expect($scope.hello).toBeDefined();
});
});
我收到以下错误:
TypeError: '[object Object]' is not a constructor (evaluating 'new User($scope.userId)')
我想问题是因为我的工厂是面向对象的,我不能使用构造函数。我跟着这个tutoarial:https://vimeo.com/90938739并做了完全相同的,唯一的区别是工厂的结构,我不想改变。
感谢您的帮助。
答案 0 :(得分:0)
问题出在这一行,因为 User 应该是构造函数而不是object。
User = jasmine.createSpyObj('User', ['load']);
以下是如何对构造函数进行模拟。
// mock constructor
var UserMock = function () {
// do some mocking..
}
User = jasmine.createSpy('User').and.callFake(UserMock);
$provide.value('User', User);
答案 1 :(得分:0)
我不确定使用spyOn是否对测试用例有用。
我会像S.Klechkovski一样创建一个模拟构造函数,但我会将它注入控制器中,如下所示:
mockUser = function () {};
mockUser.prototype.load = function () {//do your mocking promise};
ctrl = $controller('UserCtrl', { User: mockUser });
您的应用程序将使用mockUser作为用户服务,并且当您调用方法加载时,它将是模拟的。
答案 2 :(得分:0)
如果您只对代码进行了这些更改,则可以正常运行:
注释掉这些内容:
// User = jasmine.createSpyObj('User', ['load']);
// User.load.and.returnValue({
// name: 'testName'
// });
然后添加以下行:
User = function(id) { // as the other answers suggested, it is better to use a manually created fake than use a jasmine spy
return {
load: function() {
return {
then: function() { // since the load function in the real `User` returns a promise, we need this
return 'testUser ' + id;
}
};
}
};
};
// CODE
angular.module('octosendApp', [])
.factory('user', ['$http', '$q',
function($http, $q) {
var User = function(id) {
this.id = id;
this.name = null;
};
User.prototype.load = function() {
var deferred = $q.defer();
$http.post('/user/' + this.id + '/load')
.success(function(data) {
var user = new User(data.id);
user.name = data.name
deferred.resolve(user);
})
.error(function(err, code) {
deferred.reject(err);
$log.error(err, code);
});
return deferred.promise;
};
return User;
}
])
.controller('UserCtrl', ['$scope', '$routeParams', 'User',
function($scope, $routeParams, User) {
$scope.hello = "lol";
$scope.userId = $routeParams.id;
var user = new User($scope.userId);
$scope.load = function() {
user.load().then(
function(user) {
$scope.name = user.name;
},
function() {
$scope.dataFail = 'error error';
}
);
};
$scope.load();
}
]);
// SPECS
describe('Controller: UserCtrl', function() {
var $controller, $scope, User;
beforeEach(module('octosendApp', function($provide) {
// User = jasmine.createSpyObj('User', ['load']);
// User.load.and.returnValue({
// name: 'testName'
// });
User = function(id) {
return {
load: function() {
return {
then: function() {
return 'testUser ' + id;
}
};
}
};
};
$provide.value('User', User);
}));
beforeEach(inject(function(_$controller_, $rootScope, _User_) {
$scope = $rootScope.$new();
User = _User_;
$controller = _$controller_('UserCtrl', {
$scope: $scope,
User: User,
$routeParams: {
id: 2
}
});
}));
it('should have a hello property', function() {
expect($scope.hello).toBeDefined();
});
});
// Runner
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();

<link href="http://jasmine.github.io/2.3/lib/jasmine.css" rel="stylesheet" />
<script src="http://jasmine.github.io/2.3/lib/jasmine.js"></script>
<script src="http://jasmine.github.io/2.3/lib/jasmine-html.js"></script>
<script src="http://jasmine.github.io/2.3/lib/boot.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="http://code.angularjs.org/1.2.9/angular-mocks.js"></script>
&#13;
我还在答案中嵌入了一个实时测试运动员片段,这要归功于我通过Google搜索找到的this fiddle。所以你可以运行它并看到它自己。
编辑:如果我们需要在控制器中添加更多测试用例,那么这似乎是模拟User
服务的正确方法(正如我已尝试here ):
User = function (id) {
return {
load: function () {
return {
then: function (success, failure) {
success({
name: 'testUser ' + id
});
}
};
}
};
};