间谍与承诺
spyOn with Promise
我有以下我想监视的函数...但它包含一个承诺...但我收到 TypeError: 'undefined' is not an object (evaluating 'modalService.showModal({}, modalOptions).then')
因为我当然只有 spyOn(modalService,'showModal')
诺言也这样怎么算??
_modalService = {
close: function (value) { console.log(value) },
dismiss: function (value) { console.log(value) },
showModal: function (value) { console.log(value) }
};
spyOn(_modalService, 'close');
spyOn(_modalService, 'dismiss');
spyOn(_modalService, 'showModal');
控制器函数:
user.resetPassword = function () {
var modalOptions = {
closeButtonText: 'Cancel',
actionButtonText: 'Reset',
headerText: 'Reset Password',
bodyText: 'Are you sure you want to reset the users password?'
};
modalService.showModal({}, modalOptions).then(function (result) {
if (result === 'ok') {
userDataService.resetPassword(user.data).then(function (result) {
$scope.$emit('showSuccessReset');
});
};
});
};
这是我的单元测试:
it('should allow the users password to be reset', function () {
var controller = createController();
controller.resetPassword();
$httpBackend.flush();
})
*********************更新
所以我改成这样:
//Create a fake instance of the modal instance. TO ensure that the close is called
_modalService = {
close: function (value) { console.log(value) },
dismiss: function (value) { console.log(value) },
showModal: function (value) { console.log(value) }
};
spyOn(_modalService, 'close');
spyOn(_modalService, 'dismiss');
spyOn(_modalService, 'showModal').and.callThrough();
_modalService.showModal = function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
return deferred.promise;
};
说实话,虽然我不确定我能否解释清楚。虽然我理解所有异步的东西......我不确定茉莉花是如何使用它来使它全部工作的。谁能解释一下流程????我也觉得语法不对...你通常会怎么写它看起来 better/cleaner...??
当你需要模拟一个 returns promise 的函数时,你有两个选择:
- Return 一个模拟的承诺(一个类似于承诺的对象);
- Return 一个真正的承诺。
我建议 #2,因为它更容易,而且您不必担心复制整个承诺 API。换句话说,嘲笑承诺本身是不值得的。
关于 Jasmine:只有当您已经有了一个对象(不是模拟对象)并且您想监视(没有双关语意)它的一个方法时,您才需要使用 spyOn
。在您的情况下,您的整个对象都是假的,因此您可以改用 jasmine.createSpyObj
。
下面的例子应该会使以上所有内容更加清楚:
SUT
app.controller('MainCtrl', function($scope, modal, service) {
$scope.click = function() {
modal.show().then(function(result) {
if (result === 'ok') {
service.resetPassword();
}
});
};
});
测试
describe('Testing a controller', function() {
var $scope, $q,
ctrl, modalMock, serviceMock;
beforeEach(function() {
module('plunker');
modalMock = jasmine.createSpyObj('modal', ['show']);
serviceMock = jasmine.createSpyObj('service', ['resetPassword']);
inject(function($rootScope, $controller, _$q_) {
$scope = $rootScope.$new();
$q = _$q_;
ctrl = $controller('MainCtrl', {
$scope: $scope,
modal: modalMock,
service: serviceMock
});
});
});
it('should reset the password when the user confirms', function() {
// Arrange
var deferred = $q.defer();
deferred.resolve('ok');
modalMock.show.and.returnValue(deferred.promise);
// Act
$scope.click();
$scope.$digest(); // Makes Angular resolve the promise
// Assert
expect(serviceMock.resetPassword).toHaveBeenCalled();
});
it('should not reset the password when the user cancels', function() {
// Arrange
var deferred = $q.defer();
deferred.resolve('cancel');
modalMock.show.and.returnValue(deferred.promise);
// Act
$scope.click();
$scope.$digest(); // Makes Angular resolve the promise
// Assert
expect(serviceMock.resetPassword).not.toHaveBeenCalled();
});
});
可以将每个测试中的模拟编排代码移至 beforeEach
部分,以免重复。我这样做不是为了让事情简单化。
我有以下我想监视的函数...但它包含一个承诺...但我收到 TypeError: 'undefined' is not an object (evaluating 'modalService.showModal({}, modalOptions).then')
因为我当然只有 spyOn(modalService,'showModal')
诺言也这样怎么算??
_modalService = {
close: function (value) { console.log(value) },
dismiss: function (value) { console.log(value) },
showModal: function (value) { console.log(value) }
};
spyOn(_modalService, 'close');
spyOn(_modalService, 'dismiss');
spyOn(_modalService, 'showModal');
控制器函数:
user.resetPassword = function () {
var modalOptions = {
closeButtonText: 'Cancel',
actionButtonText: 'Reset',
headerText: 'Reset Password',
bodyText: 'Are you sure you want to reset the users password?'
};
modalService.showModal({}, modalOptions).then(function (result) {
if (result === 'ok') {
userDataService.resetPassword(user.data).then(function (result) {
$scope.$emit('showSuccessReset');
});
};
});
};
这是我的单元测试:
it('should allow the users password to be reset', function () {
var controller = createController();
controller.resetPassword();
$httpBackend.flush();
})
*********************更新
所以我改成这样:
//Create a fake instance of the modal instance. TO ensure that the close is called
_modalService = {
close: function (value) { console.log(value) },
dismiss: function (value) { console.log(value) },
showModal: function (value) { console.log(value) }
};
spyOn(_modalService, 'close');
spyOn(_modalService, 'dismiss');
spyOn(_modalService, 'showModal').and.callThrough();
_modalService.showModal = function() {
var deferred = $q.defer();
deferred.resolve('Remote call result');
return deferred.promise;
};
说实话,虽然我不确定我能否解释清楚。虽然我理解所有异步的东西......我不确定茉莉花是如何使用它来使它全部工作的。谁能解释一下流程????我也觉得语法不对...你通常会怎么写它看起来 better/cleaner...??
当你需要模拟一个 returns promise 的函数时,你有两个选择:
- Return 一个模拟的承诺(一个类似于承诺的对象);
- Return 一个真正的承诺。
我建议 #2,因为它更容易,而且您不必担心复制整个承诺 API。换句话说,嘲笑承诺本身是不值得的。
关于 Jasmine:只有当您已经有了一个对象(不是模拟对象)并且您想监视(没有双关语意)它的一个方法时,您才需要使用 spyOn
。在您的情况下,您的整个对象都是假的,因此您可以改用 jasmine.createSpyObj
。
下面的例子应该会使以上所有内容更加清楚:
SUT
app.controller('MainCtrl', function($scope, modal, service) {
$scope.click = function() {
modal.show().then(function(result) {
if (result === 'ok') {
service.resetPassword();
}
});
};
});
测试
describe('Testing a controller', function() {
var $scope, $q,
ctrl, modalMock, serviceMock;
beforeEach(function() {
module('plunker');
modalMock = jasmine.createSpyObj('modal', ['show']);
serviceMock = jasmine.createSpyObj('service', ['resetPassword']);
inject(function($rootScope, $controller, _$q_) {
$scope = $rootScope.$new();
$q = _$q_;
ctrl = $controller('MainCtrl', {
$scope: $scope,
modal: modalMock,
service: serviceMock
});
});
});
it('should reset the password when the user confirms', function() {
// Arrange
var deferred = $q.defer();
deferred.resolve('ok');
modalMock.show.and.returnValue(deferred.promise);
// Act
$scope.click();
$scope.$digest(); // Makes Angular resolve the promise
// Assert
expect(serviceMock.resetPassword).toHaveBeenCalled();
});
it('should not reset the password when the user cancels', function() {
// Arrange
var deferred = $q.defer();
deferred.resolve('cancel');
modalMock.show.and.returnValue(deferred.promise);
// Act
$scope.click();
$scope.$digest(); // Makes Angular resolve the promise
// Assert
expect(serviceMock.resetPassword).not.toHaveBeenCalled();
});
});
可以将每个测试中的模拟编排代码移至 beforeEach
部分,以免重复。我这样做不是为了让事情简单化。