4

我有一个返回承诺的 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

提前感谢您能给我的任何见解。

4

2 回答 2

6

您似乎误读了该解决方案。您需要调用 $rootScope.$digest() 而不是 $rootScope.$apply(); $digest 循环是导致 promise 检查它们是否被实现的原因。此外,您只需要为当前范围调用 $digest() 循环,因此您的实际调用将是 scope.$digest(),并放置在您的断言之上。

于 2014-03-09T01:00:38.737 回答
4

除了使用之外$rootScope.$digest$rootScope.$apply也可以使用 Q 库(https://github.com/kriskowal/q)作为替代品,例如:

beforeEach(function () {
    module('Module', function ($provide) {
        $provide.value('$q', Q); 
    });
});

这样可以在 $digest 循环之外解决/拒绝承诺。

于 2014-08-07T17:52:50.513 回答