Angular 测试不调用承诺回调
Angular Test does not call a promise callback
环境:
- Angular 1.5.8
- 使用 Karma/Jasmine
进行单元测试
这是我的控制器,它旨在从 $stateParams 获取一个值并使用它来执行 DELETE 请求,稍后再说。
现在它应该询问用户是否删除该对象。
这是通过 sweetalert 完成的。
我删除了 ngdoc 注释和任意 SWAL 配置。
ClientDeleteController.js:
angular.module('app.data').controller('ClientDeleteController', [
'$stateParams', '$q',
function ($stateParams, $q) {
'use strict';
var vm = this;
vm.clientId = $stateParams.id;
vm.promptDeferred = null;
vm.prompt = function () {
// create promise
var d = $q.defer();
vm.promptDeferred = d;
// create prompt
swal({ .... }, vm.swalCallback);
};
vm.swalCallback = function (confirmed) {
if (confirmed) {
console.info('resolving...');
vm.promptDeferred.resolve();
} else {
console.info('rejecting...');
vm.promptDeferred.reject();
}
};
vm.delete = function () {
vm.prompt();
vm.promptDeferred.promise.then(vm.performDelete);
};
vm.performDelete = function () {
console.info('performing');
};
}]);
这是测试套件:
ClientDeletecontrollerSpec.js
describe('Controller:ClientDeleteController', function () {
var controller
, $httpBackend
, $rootScope
, $controller
, scope
, $q
, $stateParams = {id: 1}
, resolvePromise = true
;
swal = function (options, callback) {
console.info('i am swal, this is callback', callback+'', resolvePromise);
callback(resolvePromise);
};
beforeEach(function () {
module('app');
module('app.data');
module('ui.bootstrap');
module(function ($provide) {
$provide.service('$stateParams', function () {
return $stateParams;
});
});
inject(function (_$controller_, _$rootScope_, _$q_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
$q = _$q_;
scope = $rootScope.$new();
controller = $controller('ClientDeleteController', {$scope: scope, $q: $q});
});
});
describe('basic controller features', function () {
it('should be defined', function () {
expect(controller).toBeDefined();
});
it('should get the client id from the state params', function () {
expect(controller.clientId).toBeDefined();
expect(controller.clientId).toEqual($stateParams.id);
});
});
fdescribe('client delete process', function () {
it('should ask the user if he really wants to delete the client', function () {
spyOn(controller, 'prompt').and.callThrough();
controller.delete();
expect(controller.prompt).toHaveBeenCalled();
});
it('should create a promise', function () {
controller.prompt();
expect(controller.promptDeferred).toBeDefined();
});
it('should delete when the user clicked yes', function () {
spyOn(controller, 'performDelete').and.callThrough();
controller.delete();
expect(controller.performDelete).toHaveBeenCalled();
});
it('should not delete when the user clicked no', function () {
spyOn(controller, 'performDelete').and.callThrough();
resolvePromise = false;
controller.delete();
expect(controller.performDelete).not.toHaveBeenCalled();
});
});
});
测试 should delete when the user clicked yes
失败,测试 should not delete when the user clicked no
return 是误报。
swal 模拟中的 console.info(...)
记录了正确的回调函数。函数本身的日志也被记录下来,这告诉我,回调被触发了。
因为在下一行中,我调用了 vm.promptDeferred.resolve()
resp。 .reject()
,我希望电话会真正发生。
尽管如此,测试结果是Expected spy performDelete to have been called.
。
我在另一个测试中以同样的方式模拟了 swal,它工作正常。
我不明白为什么承诺不会得到解决。
注意:当我不直接将 promise 存储在控制器上而是 return 它来自 prompt()
并使用常规 .prompt().then(...)
时,它也不会工作。
日志都是一样的,我喜欢尽可能拆分功能,这样更容易理解,更容易测试和记录。
此应用程序中还有数百个其他测试,但我不明白为什么这个不能按预期工作。
感谢您的任何见解。
如果在测试中调用 delete 后立即执行 $rootScope.$digest() 会发生什么情况?
根据我的经验,在测试期间做出 angular resolve 承诺是必要的(或者 $rootScope.$apply() 或 $httpBackend.flush())
环境:
- Angular 1.5.8
- 使用 Karma/Jasmine 进行单元测试
这是我的控制器,它旨在从 $stateParams 获取一个值并使用它来执行 DELETE 请求,稍后再说。 现在它应该询问用户是否删除该对象。 这是通过 sweetalert 完成的。
我删除了 ngdoc 注释和任意 SWAL 配置。
ClientDeleteController.js:
angular.module('app.data').controller('ClientDeleteController', [
'$stateParams', '$q',
function ($stateParams, $q) {
'use strict';
var vm = this;
vm.clientId = $stateParams.id;
vm.promptDeferred = null;
vm.prompt = function () {
// create promise
var d = $q.defer();
vm.promptDeferred = d;
// create prompt
swal({ .... }, vm.swalCallback);
};
vm.swalCallback = function (confirmed) {
if (confirmed) {
console.info('resolving...');
vm.promptDeferred.resolve();
} else {
console.info('rejecting...');
vm.promptDeferred.reject();
}
};
vm.delete = function () {
vm.prompt();
vm.promptDeferred.promise.then(vm.performDelete);
};
vm.performDelete = function () {
console.info('performing');
};
}]);
这是测试套件:
ClientDeletecontrollerSpec.js
describe('Controller:ClientDeleteController', function () {
var controller
, $httpBackend
, $rootScope
, $controller
, scope
, $q
, $stateParams = {id: 1}
, resolvePromise = true
;
swal = function (options, callback) {
console.info('i am swal, this is callback', callback+'', resolvePromise);
callback(resolvePromise);
};
beforeEach(function () {
module('app');
module('app.data');
module('ui.bootstrap');
module(function ($provide) {
$provide.service('$stateParams', function () {
return $stateParams;
});
});
inject(function (_$controller_, _$rootScope_, _$q_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
$q = _$q_;
scope = $rootScope.$new();
controller = $controller('ClientDeleteController', {$scope: scope, $q: $q});
});
});
describe('basic controller features', function () {
it('should be defined', function () {
expect(controller).toBeDefined();
});
it('should get the client id from the state params', function () {
expect(controller.clientId).toBeDefined();
expect(controller.clientId).toEqual($stateParams.id);
});
});
fdescribe('client delete process', function () {
it('should ask the user if he really wants to delete the client', function () {
spyOn(controller, 'prompt').and.callThrough();
controller.delete();
expect(controller.prompt).toHaveBeenCalled();
});
it('should create a promise', function () {
controller.prompt();
expect(controller.promptDeferred).toBeDefined();
});
it('should delete when the user clicked yes', function () {
spyOn(controller, 'performDelete').and.callThrough();
controller.delete();
expect(controller.performDelete).toHaveBeenCalled();
});
it('should not delete when the user clicked no', function () {
spyOn(controller, 'performDelete').and.callThrough();
resolvePromise = false;
controller.delete();
expect(controller.performDelete).not.toHaveBeenCalled();
});
});
});
测试 should delete when the user clicked yes
失败,测试 should not delete when the user clicked no
return 是误报。
swal 模拟中的 console.info(...)
记录了正确的回调函数。函数本身的日志也被记录下来,这告诉我,回调被触发了。
因为在下一行中,我调用了 vm.promptDeferred.resolve()
resp。 .reject()
,我希望电话会真正发生。
尽管如此,测试结果是Expected spy performDelete to have been called.
。
我在另一个测试中以同样的方式模拟了 swal,它工作正常。 我不明白为什么承诺不会得到解决。
注意:当我不直接将 promise 存储在控制器上而是 return 它来自 prompt()
并使用常规 .prompt().then(...)
时,它也不会工作。
日志都是一样的,我喜欢尽可能拆分功能,这样更容易理解,更容易测试和记录。
此应用程序中还有数百个其他测试,但我不明白为什么这个不能按预期工作。
感谢您的任何见解。
如果在测试中调用 delete 后立即执行 $rootScope.$digest() 会发生什么情况?
根据我的经验,在测试期间做出 angular resolve 承诺是必要的(或者 $rootScope.$apply() 或 $httpBackend.flush())