programing

Angularjs 약속이 단위 테스트에서 해결되지 않음

muds 2023. 9. 18. 22:43
반응형

Angularjs 약속이 단위 테스트에서 해결되지 않음

저는 jasmine을 사용하여 약속 객체를 반환하는 서비스 메서드를 호출한 결과에 범위의 변수를 설정하는 angularjs 컨트롤러를 단위 테스트하고 있습니다.

var MyController = function($scope, service) {
    $scope.myVar = service.getStuff();
}

서비스 내부:

function getStuff() {
    return $http.get( 'api/stuff' ).then( function ( httpResult ) {
        return httpResult.data;
    } );
}

이것은 제 angularjs 어플리케이션의 상황에서는 잘 작동하지만 재스민 단위 테스트에서는 작동하지 않습니다.유닛 테스트에서 "그때" 콜백이 실행되고 있음을 확인했지만 $scope.myVar 약속은 콜백의 반환 값으로 설정되지 않습니다.

내 유닛 테스트:

describe( 'My Controller', function () {
  var scope;
  var serviceMock;
  var controller;
  var httpBackend;

  beforeEach( inject( function ( $rootScope, $controller, $httpBackend, $http ) {
    scope = $rootScope.$new();
    httpBackend = $httpBackend;
    serviceMock = {
      stuffArray: [{
        FirstName: "Robby"
      }],

      getStuff: function () {
        return $http.get( 'api/stuff' ).then( function ( httpResult ) {
          return httpResult.data;
        } );
      }
    };
    $httpBackend.whenGET( 'api/stuff' ).respond( serviceMock.stuffArray );
    controller = $controller( MyController, {
      $scope: scope,
      service: serviceMock
    } );
  } ) );

  it( 'should set myVar to the resolved promise value',
    function () {
      httpBackend.flush();
      scope.$root.$digest();
      expect( scope.myVar[0].FirstName ).toEqual( "Robby" );
    } );
} );

또한 컨트롤러를 다음과 같이 변경하면 유닛 테스트 합격:

var MyController = function($scope, service) {
    service.getStuff().then(function(result) {
        $scope.myVar = result;
    });
}

단위 테스트에서 약속 콜백 결과 값이 $scope.myVar로 전파되지 않는 이유는 무엇입니까?전체 작동 코드는 다음 jsfiddle 참조 http://jsfiddle.net/s7PGg/5/

이 '미스터리'의 핵심은 앵글이JS는 템플릿에서 보간 지시어에 사용된 약속을 자동으로 해결하고 결과를 렌더링합니다.제 말은 이 컨트롤러를 고려하면 다음과 같습니다.

MyCtrl = function($scope, $http) {
  $scope.promise = $http.get('myurl', {..});
}

템플릿:

<span>{{promise}}</span>

$http 호출이 완료되면 AngularJS는 약속이 해결된 것을 "확인"하고 해결된 결과로 템플릿을 다시 렌더링합니다.$q 문서에서 모호하게 언급된 내용은 다음과 같습니다.

$q 약속은 템플릿 엔진에서 각도로 인식됩니다. 즉, 템플릿에서는 범위에 연결된 약속을 결과 값인 것처럼 처리할 수 있습니다.

이 마법이 일어나는 코드는 여기서 볼 수 있습니다.

" " " 이 " " 만 " 합니다 " " " " 합니다 " " " 만 " " " "$parse서비스, 더 정확히 말하자면) 경기 중입니다.단위 테스트에는 템플릿이 포함되어 있지 않으므로 약속 해결이 자동으로 전파되지 않습니다.

이 질문에서 알 수 있듯이 자동 해결/결과 전파는 매우 편리하지만 혼란스러울 수 있습니다.이것이 제가 여러분이 했던 것처럼 해결 결과를 명시적으로 전파하는 것을 선호하는 이유입니다.

var MyController = function($scope, service) {
    service.getStuff().then(function(result) {
        $scope.myVar = result;
    });
}

저도 비슷한 문제가 있어서 컨트롤러가 $scope.myVar를 약속에 직접 할당하도록 했습니다.그리고 시험에서는 그 약속이 해결되면 기대되는 가치를 주장하는 또 다른 약속을 사슬로 묶었습니다.나는 다음과 같은 도우미 방법을 사용했습니다.

var expectPromisedValue = function(promise, expectedValue) {
  promise.then(function(resolvedValue) {
    expect(resolvedValue).toEqual(expectedValue);
  });
}

expectPromiseValue를 호출할 때의 순서와 테스트 중인 코드에 의해 약속이 해결될 때의 순서에 따라 최종 다이제스트 사이클을 수동으로 트리거하여 실행해야 할 수도 있습니다. 이 방법이 없으면 테스트가 통과될 수 있습니다.resolvedValue과 동등한expectedValue그렇지 않으면.

안전을 위해 트리거를 후() 통화에 넣어 모든 테스트에서 트리거를 기억할 필요가 없습니다.

afterEach(inject(function($rootScope) {
  $rootScope.$apply();
}));

@pkozlowski.opensource는 그 이유(THANK YOU!)에 대해 답했지만 테스트에서 이 문제를 해결하는 방법은 답하지 않았습니다.

방금 도착한 해결책은 HTTP가 서비스에서 호출되고 있는지 테스트한 후 컨트롤러 테스트에서 서비스 방식을 염탐하여 약속 대신 실제 값을 반환하는 것입니다.

서버와 대화하는 사용자 서비스가 있다고 가정합니다.

var services = angular.module('app.services', []);

services.factory('User', function ($q, $http) {

  function GET(path) {
    var defer = $q.defer();
    $http.get(path).success(function (data) {
      defer.resolve(data);
    }
    return defer.promise;
  }

  return {
    get: function (handle) {
      return GET('/api/' + handle);    // RETURNS A PROMISE
    },

    // ...

  };
});

해당 서비스를 테스트해 보면 반환된 값이 어떻게 되든 상관없이 HTTP 호출만 제대로 이루어졌을 뿐입니다.

describe 'User service', ->
  User = undefined
  $httpBackend = undefined

  beforeEach module 'app.services'

  beforeEach inject ($injector) ->
    User = $injector.get 'User'
    $httpBackend = $injector.get '$httpBackend'

  afterEach ->
    $httpBackend.verifyNoOutstandingExpectation()
    $httpBackend.verifyNoOutstandingRequest()          

  it 'should get a user', ->
    $httpBackend.expectGET('/api/alice').respond { handle: 'alice' }
    User.get 'alice'
    $httpBackend.flush()    

이제 컨트롤러 테스트에서는 HTTP에 대해 걱정할 필요가 없습니다.사용자 서비스가 실행되고 있는지 확인하고 싶을 뿐입니다.

angular.module('app.controllers')
  .controller('UserCtrl', function ($scope, $routeParams, User) {
    $scope.user = User.get($routeParams.handle);
  }); 

이를 테스트하기 위해 우리는 User 서비스를 염탐합니다.

describe 'UserCtrl', () ->

  User = undefined
  scope = undefined
  user = { handle: 'charlie', name: 'Charlie', email: 'charlie@example.com' }

  beforeEach module 'app.controllers'

  beforeEach inject ($injector) ->
    # Spy on the user service
    User = $injector.get 'User'
    spyOn(User, 'get').andCallFake -> user

    # Other service dependencies
    $controller = $injector.get '$controller'
    $routeParams = $injector.get '$routeParams'
    $rootScope = $injector.get '$rootScope'
    scope = $rootScope.$new();

    # Set up the controller
    $routeParams.handle = user.handle
    UserCtrl = $controller 'UserCtrl', $scope: scope

  it 'should get the user by :handle', ->
    expect(User.get).toHaveBeenCalledWith 'charlie'
    expect(scope.user.handle).toBe 'charlie';

약속을 해결할 필요는 없습니다.도움이 되길 바랍니다.

언급URL : https://stackoverflow.com/questions/15048132/angularjs-promise-not-being-resolved-in-unit-test

반응형