我正在尝试使用Jasmine为我的AngularJS用户服务开发单元测试,该服务依赖于$ resource。我的测试是:
'use strict';
describe("User Service Test", function() {
var service;
var mockLoginUser = { email: 'hidden', password: "hidden" };
beforeEach(module('flightlottery.userApi'));
beforeEach(inject(function(User) {
service = User;
// $scope = _$scope_;
//http = $httpBacked;
}));
it('should fetch login a user', function(done) {
var testUser = function(user) {
console.log('callback called');
//expect(user.email).toBe(mockLoginUser.email);
//expect(user.password).toBe(mockUser.password);
};
var failTest = function(error) {
expect(error).toBeUndefined();
};
//http.expectPost('/users/login', mockLoginUser).respond(200,'');
//http.expectGET('/employees/1').respond(200,mockEmployee);
service.login(mockLoginUser)
.$promise.then(testUser)
.catch(failTest)
.finally(done);
// $scope.$apply;
//http.flush();
});
});
当我运行测试时,我收到以下错误。
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
任何人都可以告诉我如何做到这一点?我觉得它很混乱。
谢谢!
***编辑: 这是我的用户服务。我想测试登录方法。
angular.module('flightlottery.userApi', ['ngResource']).
factory('User', function($resource, $rootScope) {
var current_user;
var User = $resource('http://somesite.ca/api/users/:method/:id', {}, {
query: {method:'GET', params: {method:'index'}, isArray:true },
save: {method:'POST', params: {method:'save'} },
get: {method:'GET', params: {method:'edit'} },
phistory: {method:'GET', params: {method:'history'}, isArray:true },
remove: {method:'DELETE', params: {method:'remove'} },
login: {method:'POST', params: {method:'login'} },
logout: {method:'POST', params: {method:'logout'} },
register: {method:'POST', params: {method:'register'} }
});
User.setCurrentUser = function(user) {
//var self = this;
current_user = user;
$rootScope.$broadcast('user:updated',user);
//console.log(self.current_user_id);
}
User.getCurrentUser = function() {
//var self = this;
return current_user;
//console.log(self.current_user_id);
}
User.registerUser = function(cb) {
//console.log(cb);
return User.register(cb);
}
User.play_history = function(cb) {
//console.log(cb);
return User.phistory(cb);
}
User.loginUser = function(cb) {
return User.login(cb);
}
User.logoutUser = function(cb) {
current_user = null;
return User.logout(cb);
// return User.logout();
///return User.save({id: this.id},
//angular.extend({}, this, {id:undefined}), cb);
};
User.prototype.update = function(cb) {
return User.save({id: this.id},
angular.extend({}, this, {id:undefined}), cb);
};
User.prototype.destroy = function(cb) {
return User.remove({id: this.id}, cb);
};
return User;
});
答案 0 :(得分:2)
在尝试猜测你想要完成什么之后,几乎没有重构 - 结果看起来像这样。
我总是想知道我们想要测试什么,以及我们想要证明测试通过或失败的原因。
带有依赖关系的版本
angular.module('flightlottery.userApi', ['ngResource'])
.factory('User', function($resource) {
var User = $resource('http://somesite.ca/api/users/:method/:id', {}, {
login: {
method: 'POST',
params: {
method: 'login'
}
}
});
User.loginUser = function(cb) {
return User.login(cb);
}
return User;
})
describe("User Service Test", function() {
var service;
var queryDeferred;
var mockLoginUser = {
email: 'hidden',
password: "hidden"
};
var scenarios = {
success: function(user) {
expect(user.email).toBe(mockLoginUser.email);
expect(user.password).toBe(mockLoginUser.password);
},
fail: function(error) {
expect(error).toBeDefined();
}
}
beforeEach(module('flightlottery.userApi'));
beforeEach(inject(function(_$rootScope_) {
$rootScope = _$rootScope_;
}));
beforeEach(inject(function($q) {
queryDeferred = $q.defer();
mockUserLogin = {
login: function() {
return {
$promise: queryDeferred.promise
};
}
}
spyOn(mockUserLogin, 'login').and.callThrough();
spyOn(scenarios, 'success').and.callThrough();
spyOn(scenarios, 'fail').and.callThrough();
}))
it('runs `success scenario` if user object is fetched', function() {
queryDeferred.resolve(mockLoginUser)
userLogin(mockLoginUser, scenarios);
expect(scenarios.success).toHaveBeenCalled()
expect(scenarios.fail).not.toHaveBeenCalled()
});
it('runs `fail scenario` if user object is not fetched', function() {
var reason = {
error: 'some error'
}
queryDeferred.reject(reason)
userLogin(mockLoginUser, scenarios)
expect(scenarios.success).not.toHaveBeenCalled()
expect(scenarios.fail).toHaveBeenCalledWith(reason)
});
function userLogin(mockLoginUser, scenarios) {
mockUserLogin.login(mockLoginUser)
.$promise.then(scenarios.success)
.catch(scenarios.fail)
.finally();
$rootScope.$apply();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-resource.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>
第二版 - 注入服务和$ httpBackend
当Angular应用程序需要来自服务器的某些数据时,它会调用$ http服务,该服务使用$ httpBackend服务将请求发送到真实服务器。使用依赖注入,很容易注入$ httpBackend mock(它具有与$ httpBackend相同的API)并使用它来验证请求并使用一些测试数据进行响应,而无需向真实服务器发送请求。
angular.module('flightlottery.userApi', ['ngResource']).
factory('User', function($resource, $rootScope) {
var User = $resource('http://somesite.ca/api/users/:method/:id', {}, {
login: {
method: 'POST',
params: {
method: 'login'
}
}
});
User.loginUser = function(cb) {
return User.login(cb);
}
return User;
});
describe("User Service Test", function() {
var $httpBackend, User;
beforeEach(module('flightlottery.userApi'));
beforeEach(inject(function(_$httpBackend_, _User_) {
$httpBackend = _$httpBackend_;
User = _User_;
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('calls `POST` method to interacts with backend', function() {
var stubUser = {
name: 'Some name',
password: 'somePassword'
};
var stubResponse = {
login: 'someName',
lastLogin: Date.now()
}
spyOn(User, 'login').and.callThrough();
User.loginUser(stubUser).$promise.then(function(response) { // response is stubbed by second argument of repond method
expect(response.login).toBe(stubResponse.login)
expect(response.lastLogin).toBe(stubResponse.lastLogin)
});
expect(User.login).toHaveBeenCalledWith(stubUser)
$httpBackend.expectPOST('http://somesite.ca/api/users/login').respond(200, stubResponse)
$httpBackend.flush();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-resource.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>