我正在为一个控制器编写一个单元测试,该控制器具有一个角度范围函数,当调用它时,调用一个parse.com框架函数,该函数调用解析服务器并返回成功参数或错误代码。
如何在单元测试中模拟该对象?
控制器范围内的函数名称是Parse.User.logIn,这是我到目前为止所做的模拟对象。
mockParse = {
User: {
logIn: function(_userName, _password, callbackObj) {
callbackObj.success({
attributes:{
username: "testuser",
email: "testuser@example.com"
}
$rootScope.user = _user;
$rootScope.isLoggedIn = true;
$state.go('tab.home');
};
callbackObj.error({
err:{
code: 101
}
if (err.code == 101){
$scope.error.message = "invalid login credentials";
} else {
$scope.error.message = "Unexpected error"
}
});
}
}
}
我做对了吗?我是否必须为不同的回调响应,错误和成功制作不同的对象?
当我把它放入测试时,我在哪里注射它?我是否将它放在控制器中,如下所示?:
ctrl = $controller("LoginCtrl", {
$scope: $scope,
Parse: mockParse
});
这是实际的功能:
Parse.User.logIn(($scope.user.username) , $scope.user.password, {
success: function(_user) {
console.log('Login Success');
console.log('user = ' + _user.attributes.username);
console.log('email = ' + _user.attributes.email);
$ionicLoading.hide();
$rootScope.user = _user;
$rootScope.isLoggedIn = true;
$state.go('tab.home');
},
error: function(user, err) {
$ionicLoading.hide();
// The login failed. Check error to see why.
if (err.code === 101) {
$scope.error.message = 'Invalid login credentials';
} else {
$scope.error.message = 'An unexpected error has ' +
'occurred, please try again.';
}
console.log('error message on Parse.User.logIn: ' + $scope.error.message);
$scope.$apply();
}
});
答案 0 :(得分:0)
我建议你去抓sinonjs - 超级有效且(经过一段时间)易于用于嘲弄/剔除/间谍。
首先不要担心Parse
对象的内部实现,我们只在单元测试设置中对input
和output
依赖项感兴趣。没有理由在该精细等级细节中存根服务对象。
beforeEach
var $scope, Parse, createController;
beforeEach(function () {
Parse = {};
module('your_module', function ($provide) {
// Provide "Parse" as a value in your module.
$provide.value('Parse', Parse);
});
inject(function ($controller, $injector) {
$scope = $injector.get('$rootScope').$new();
Parse = $injector.get('Parse'); // equal to: {} (at this point)
createController = function () {
return $controller('LoginCtrl', {
$scope: $scope,
Parse: Parse
});
};
});
Parse.user = {};
Parse.user.login = sinon.stub();
});
Parse现在完全被删除了。它将是一个空对象,然后提供给模块,在我们的规范中公开,注入到我们的控制器中,然后使用与实际实现匹配的存根方法进行修饰(当然,仅通过名称)。
您应该在控制器规范中测试的内容不是Parse
服务的内部行为,而是您的控制器使用正确的参数调用Parse
的方法。
之后,您可以继续测试您的控制器,以及其关联的$ scope 对Parse.user.login()
响应做出的反应。
注意:我正在使用mocha / chai语法编写这些规范 - 采用您认为合适的茉莉风格(不确定它与sinon.js的关系如何相当诚实)
it('calls the Parse method', function () {
createController();
$scope.login('username', 'pw', angular.noop);
expect(Parse.user.login).to.have
.been.calledOnce.and.calledWith('username', 'pw', angular.noop);
});
context('Parse response', function () {
it('failed', function () {
Parse.user.login.returns({ status: 'super_error' });
createController();
$scope.login('a', 'b', angular.noop);
expect(/* expect that something on $scope happened? */);
});
it('was succesful', function () {
Parse.user.login.returns({ status: 'great_success', data: {} });
createController();
$scope.login('a', 'b', angular.noop);
expect(/* expect that something on $scope happened? */);
});
});
这就是我写它的方式。但是 - 查看Parse.user.logIn docs我们可以看到该方法返回promise
。
因此,您需要采取以下步骤来有效地删除正确的行为:
var $q;
// combine this beforeEach block with the first one we wrote.
beforeEach(function () {
inject(function ($injector) {
$q = $injector.get('$q');
});
});
context('parse.user.login', inject(function ($timeout) {
var success, error, opts;
beforeEach(function () {
success = sinon.stub();
error = sinon.stub();
opts = {
success: success,
error: error
};
});
it('failed', function () {
Parse.user.logIn.returns($q.reject({ status: 'bad_error' });
createController();
$scope.login('a', 'b', opts);
$timeout.flush(); // Either this, or $rootScope.$digest or .notify(done) to flush the promise.
expect(error).to.have.been.calledOnce;
});
it('was successful', function () {
Parse.user.logIn.returns($q.when({ status: 'yay!', data: {} });
createController();
$scope.login('a', 'b', opts);
$timeout.flush(); // Either this, or $rootScope.$digest or .notify(done) to flush the promise.
expect(success).to.have.been.calledOnce;
});
}));