我有一个返回承诺的 AngularJS 服务。
尽管代码运行良好,但测试给我带来了一些困难,因为在我的单元测试中从未调用过 promise 的“then”方法。
常见的答案似乎是“在 JasmineJS 测试中未触发 AngularJS Promise Callback Not Trigged$rootScope.$apply()
”一文中提到的调用。但是,如果我这样做,我的测试会尝试进行 load ,这不是预期的:templates/home.html
PhantomJS 1.9.7 (Linux) Controller: ScanCtrl should invoke the barcode scanner when it is available FAILED
Error: Unexpected request: GET templates/home.html
No more request expected
如果我未能 include $rootScope.$apply()
,则永远不会调用 promise 的“then”方法,并且我会收到一个错误,即我的 spy 未按预期调用:
PhantomJS 1.9.7 (Linux) Controller: ScanCtrl should invoke the barcode scanner when it is available FAILED
Expected spy go to have been called with [ 'enterQuantity', { barcodeId : '888888888888' } ] but it was never called.
所以我的问题是——我应该如何让这些测试发挥作用?有什么办法可以避免打电话$rootScope.$apply()
吗?或者,我是否需要想办法让我的代码不尝试去templates/home.html
何时$rootScope.$apply()
被调用?
服务
.factory('BarcodeScannerService', ['$q', function ($q) {
return {
scanBarcode: function () {
var deferred = $q.defer();
plugins.barcodeScanner.scan(
function (result) {
console.log("We got a barcode\n" +
"Result: " + result.text + "\n" +
"Format: " + result.format + "\n" +
"Cancelled: " + result.cancelled);
deferred.resolve({"error": false, "barcode": result.text});
},
function (error) {
deferred.resolve({"error": true});
});
return deferred.promise;
}
};
}]
)
单元测试
it('should invoke the barcode scanner when it is available', function () {
inject(function ($controller, $rootScope, $q) {
scope = $rootScope.$new();
$rootScopeHolder = $rootScope;
var deferred = $q.defer();
barcodeScannerServiceMock.scanBarcode = jasmine.createSpy('scanBarcode').andReturn(deferred.promise);
deferred.resolve({"error": false, "barcode": fakeBarcode2});
barcodeScannerServiceMock.isAvailable = jasmine.createSpy('isAvailable').andReturn(true);
//$scope, $timeout, Items, $state, SubmitCartService, $window
ScanCtrl = $controller('ScanCtrl', {
$scope: scope,
$window: windowMock,
Items: itemMock,
BarcodeScannerService: barcodeScannerServiceMock,
$state: stateMock
});
});
expect(barcodeScannerServiceMock.isAvailable).toHaveBeenCalled();
expect(barcodeScannerServiceMock.scanBarcode).toHaveBeenCalled();
//$rootScopeHolder.$apply();
expect(stateMock.go).toHaveBeenCalledWith('enterQuantity', { barcodeId: fakeBarcode2 });
});
被测控制器
.controller('ScanCtrl', function ($scope, $timeout, $ionicModal, $state, BarcodeScannerService, $window) {
$scope.handleBarcodeScanError = function () {
var r = $window.confirm("Scanning failed. Try again?");
if (r === true) {
$state.go('scan');
}
else {
$state.go('home');
}
};
console.log("Scanner Avaialble?" + BarcodeScannerService.isAvailable());
if (BarcodeScannerService.isAvailable() === true) {
var barcodeResult = {};
BarcodeScannerService.scanBarcode()
.then(function(result){
barcodeResult = result;
if (barcodeResult.error === false) {
$state.go('enterQuantity', {barcodeId: barcodeResult.barcode});
}
else {
$scope.handleBarcodeScanError();
}
}, function(error){
$scope.handleBarcodeScanError();
});
}
//else, if barcode scanner is not available ask them to key it in
else {
var tempBarcode = $window.prompt('Enter barcode:');
$state.go('enterQuantity', {barcodeId: tempBarcode});
}
}
)
完整代码在这里: https ://github.com/derekdata/barcode-cart-builder/
控制器:www/js/app.js
服务:www/js/services/services.js
测试:www_test/spec/controllers/ScanCtrlTest.js
提前感谢您能给我的任何见解。