programing

Angular에서의 $modal 조롱JS 유닛 테스트

subpage 2023. 2. 23. 22:48
반응형

Angular에서의 $modal 조롱JS 유닛 테스트

난 지금 콘트롤러를 작동시키는 장치 테스트를 쓰고 있어$modal반환된 약속을 사용하여 논리를 실행합니다.$modal을 실행하는 부모 컨트롤러는 테스트할 수 있지만 성공 약속을 조롱하는 방법은 아무리 생각해도 찾을 수 없습니다.

여러 가지 방법을 시도해 봤는데$q그리고.$scope.$apply()약속의 해결을 강요하는 것.하지만 가장 근접한 것은 이 SO 포스트의 마지막 답변과 유사한 내용을 정리한 것입니다.

'오래된'에게 이런 질문을 받는 걸 몇 번 본 적이 있어요$dialogmodal. "new"를 어떻게 사용하는지 잘 모르겠어요.$dialog모달

몇 가지 조언은 감사할 것입니다.

문제를 설명하기 위해 UI 부트스트랩 문서에 제공된 예제를 사용하고 있으며, 몇 가지 사소한 편집도 하고 있습니다.

컨트롤러(메인 및 모듈)

'use strict';

angular.module('angularUiModalApp')
    .controller('MainCtrl', function($scope, $modal, $log) {
        $scope.items = ['item1', 'item2', 'item3'];

        $scope.open = function() {

            $scope.modalInstance = $modal.open({
                templateUrl: 'myModalContent.html',
                controller: 'ModalInstanceCtrl',
                resolve: {
                    items: function() {
                        return $scope.items;
                    }
                }
            });

            $scope.modalInstance.result.then(function(selectedItem) {
                $scope.selected = selectedItem;
            }, function() {
                $log.info('Modal dismissed at: ' + new Date());
            });
        };
    })
    .controller('ModalInstanceCtrl', function($scope, $modalInstance, items) {
        $scope.items = items;
        $scope.selected = {
            item: $scope.items[0]
        };

        $scope.ok = function() {
            $modalInstance.close($scope.selected.item);
        };

        $scope.cancel = function() {
            $modalInstance.dismiss('cancel');
        };
    });

뷰(main.html)

<div ng-controller="MainCtrl">
    <script type="text/ng-template" id="myModalContent.html">
        <div class="modal-header">
            <h3>I is a modal!</h3>
        </div>
        <div class="modal-body">
            <ul>
                <li ng-repeat="item in items">
                    <a ng-click="selected.item = item">{{ item }}</a>
                </li>
            </ul>
            Selected: <b>{{ selected.item }}</b>
        </div>
        <div class="modal-footer">
            <button class="btn btn-primary" ng-click="ok()">OK</button>
            <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
        </div>
    </script>

    <button class="btn btn-default" ng-click="open()">Open me!</button>
    <div ng-show="selected">Selection from a modal: {{ selected }}</div>
</div>

테스트

'use strict';

describe('Controller: MainCtrl', function() {

    // load the controller's module
    beforeEach(module('angularUiModalApp'));

    var MainCtrl,
        scope;

    var fakeModal = {
        open: function() {
            return {
                result: {
                    then: function(callback) {
                        callback("item1");
                    }
                }
            };
        }
    };

    beforeEach(inject(function($modal) {
        spyOn($modal, 'open').andReturn(fakeModal);
    }));


    // Initialize the controller and a mock scope
    beforeEach(inject(function($controller, $rootScope, _$modal_) {
        scope = $rootScope.$new();
        MainCtrl = $controller('MainCtrl', {
            $scope: scope,
            $modal: _$modal_
        });
    }));

    it('should show success when modal login returns success response', function() {
        expect(scope.items).toEqual(['item1', 'item2', 'item3']);

        // Mock out the modal closing, resolving with a selected item, say 1
        scope.open(); // Open the modal
        scope.modalInstance.close('item1');
        expect(scope.selected).toEqual('item1'); 
        // No dice (scope.selected) is not defined according to Jasmine.
    });
});

각각에서 $modal.open 함수를 감시할 때

spyOn($modal, 'open').andReturn(fakeModal);

or 

spyOn($modal, 'open').and.returnValue(fakeModal); //For Jasmine 2.0+

$modal.open이 보통 반환하는 것에 대한 모의값을 반환해야 합니다. $modal의 모의값은 반환되지 않습니다.open에서 설명한 대로 기능합니다.fakeModal가짜 모달은 분명 가짜 모달은result를 포함하는 오브젝트then콜백을 저장하는 함수([OK]버튼 또는 [Cancel]버튼이 클릭되었을 때 호출됩니다).또,close기능(모달에서 OK 버튼 클릭 시뮬레이션) 및dismiss기능(모달에서 Cancel 버튼을 클릭하는 시뮬레이션)close그리고.dismiss함수는 호출 시 필요한 콜백 함수를 호출합니다.

를 변경하다fakeModal유닛 테스트에 합격합니다.

var fakeModal = {
    result: {
        then: function(confirmCallback, cancelCallback) {
            //Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
            this.confirmCallBack = confirmCallback;
            this.cancelCallback = cancelCallback;
        }
    },
    close: function( item ) {
        //The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
        this.result.confirmCallBack( item );
    },
    dismiss: function( type ) {
        //The user clicked cancel on the modal dialog, call the stored cancel callback
        this.result.cancelCallback( type );
    }
};

또한 취소 핸들러에서 테스트할 속성을 추가하여 취소 대화 상자를 테스트할 수 있습니다.$scope.canceled:

$scope.modalInstance.result.then(function (selectedItem) {
    $scope.selected = selectedItem;
}, function () {
    $scope.canceled = true; //Mark the modal as canceled
    $log.info('Modal dismissed at: ' + new Date());
});

취소 플래그가 설정되면 장치 테스트는 다음과 같습니다.

it("should cancel the dialog when dismiss is called, and $scope.canceled should be true", function () {
    expect( scope.canceled ).toBeUndefined();

    scope.open(); // Open the modal
    scope.modalInstance.dismiss( "cancel" ); //Call dismiss (simulating clicking the cancel button on the modal)
    expect( scope.canceled ).toBe( true );
});

Brant의 답변에 덧붙이자면 다른 시나리오에 대처할 수 있도록 약간 개선된 모의실험을 소개합니다.

var fakeModal = {
    result: {
        then: function (confirmCallback, cancelCallback) {
            this.confirmCallBack = confirmCallback;
            this.cancelCallback = cancelCallback;
            return this;
        },
        catch: function (cancelCallback) {
            this.cancelCallback = cancelCallback;
            return this;
        },
        finally: function (finallyCallback) {
            this.finallyCallback = finallyCallback;
            return this;
        }
    },
    close: function (item) {
        this.result.confirmCallBack(item);
    },
    dismiss: function (item) {
        this.result.cancelCallback(item);
    },
    finally: function () {
        this.result.finallyCallback();
    }
};

이를 통해 모의는 ...의 상황을 처리할 수 있다.

모달과.then(),.catch()그리고..finally()핸들러 스타일 대신 2개의 기능을 전달합니다(successCallback, errorCallback)부터 a까지.then()예를 들어 다음과 같습니다.

modalInstance
    .result
    .then(function () {
        // close hander
    })
    .catch(function () {
        // dismiss handler
    })
    .finally(function () {
        // finally handler
    });

모달은 약속을 사용하기 때문에 반드시 $q를 사용해야 합니다.

코드는 다음과 같습니다.

function FakeModal(){
    this.resultDeferred = $q.defer();
    this.result = this.resultDeferred.promise;
}
FakeModal.prototype.open = function(options){ return this;  };
FakeModal.prototype.close = function (item) {
    this.resultDeferred.resolve(item);
    $rootScope.$apply(); // Propagate promise resolution to 'then' functions using $apply().
};
FakeModal.prototype.dismiss = function (item) {
    this.resultDeferred.reject(item);
    $rootScope.$apply(); // Propagate promise resolution to 'then' functions using $apply().
};

// ....

// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    fakeModal = new FakeModal();
    MainCtrl = $controller('MainCtrl', {
        $scope: scope,
        $modal: fakeModal
   });
}));

// ....

it("should cancel the dialog when dismiss is called, and  $scope.canceled should be true", function () {
    expect( scope.canceled ).toBeUndefined();

    fakeModal.dismiss( "cancel" ); //Call dismiss (simulating clicking the cancel button on the modal)
    expect( scope.canceled ).toBe( true );
});

Brant의 답변은 분명 훌륭했지만, 이 변경으로 인해 저는 더 좋아졌습니다.

  fakeModal =
    opened:
      then: (openedCallback) ->
        openedCallback()
    result:
      finally: (callback) ->
        finallyCallback = callback

테스트 영역에서 다음을 수행합니다.

  finallyCallback()

  expect (thing finally callback does)
    .toEqual (what you would expect)

언급URL : https://stackoverflow.com/questions/21214868/mocking-modal-in-angularjs-unit-tests

반응형