programing

유닛 테스트에서의 bindToController

linuxpc 2023. 3. 17. 19:39
반응형

유닛 테스트에서의 bindToController

bindToController를 사용하여 격리된 스코프를 컨트롤러에 직접 연결하도록 지시하고 있습니다.이 명령어는 다음과 같습니다.

app.directive('xx', function () {
  return {
    bindToController: true,
    controller: 'xxCtrl',
    scope: {
      label: '@',
    },
  };
});

다음으로 HTML에서 라벨이 지정되지 않은 경우 컨트롤러에 기본값이 있습니다.

app.controller('xxCtrl', function () {
  var ctrl = this;

  ctrl.label = ctrl.label || 'default value';
});

재스민 유닛 테스트에서 xxCtrl을 인스턴스화해서 ctrl.label을 테스트하려면 어떻게 해야 하나요?

describe('buttons.RemoveButtonCtrl', function () {
  var ctrl;

  beforeEach(inject(function ($controller) {
    // What do I do here to set ctrl.label BEFORE the controller runs?
    ctrl = $controller('xxCtrl');
  }));

  it('should have a label', function () {
    expect(ctrl.label).toBe('foo');
  });
});

문제를 테스트하려면 선택하십시오.

각도 1.3(아래 1.4+ 참조)

앵귤러 파고들기JS 소스 코드 문서화되어 있지 않은 세 번째 인수를 찾았습니다.$controller착신측 서비스later($controller 소스 참조).

사실이라면$controller()속성이 있는 함수를 반환합니다.instance속성을 설정할 수 있습니다.
컨트롤러를 인스턴스화할 준비가 되면 함수를 호출하면 생성자에서 사용할 수 있는 속성을 사용하여 컨트롤러가 인스턴스화됩니다.

이 예에서는 다음과 같이 동작합니다.

describe('buttons.RemoveButtonCtrl', function () {

  var ctrlFn, ctrl, $scope;

  beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();

    ctrlFn = $controller('xxCtrl', {
      $scope: scope,
    }, true);
  }));

  it('should have a label', function () {
    ctrlFn.instance.label = 'foo'; // set the value

    // create controller instance
    ctrl = ctrlFn();

    // test
    expect(ctrl.label).toBe('foo');
  });

});

업데이트된 Plunker(작동하려면 Angular를 업그레이드해야 합니다.지금은 1.3.0~rc.4입니다) : http://plnkr.co/edit/tnLIyzZHKqPO6Tekd804?p=preview

Angular 소스 코드를 인용하여 사용하는 것은 권장되지 않습니다.

나중에 컨트롤러 인스턴스화:이 기계는 컨트롤러의 생성자 자체를 호출하기 전에 개체의 인스턴스를 만드는 데 사용됩니다.

이를 통해 컨스트럭터를 호출하기 전에 컨트롤러에 속성을 추가할 수 있습니다.주로 스코프 바인딩을 $compile로 분리하기 위해 사용됩니다.

이 기능은 응용 프로그램에서 사용하도록 설계되지 않았기 때문에 공개적으로 문서화되어 있지 않습니다.

단, 컨트롤러를 테스트하는 메커니즘이 없습니다.bindToController: true그래도 쓰게 해줬는데..앵귤러 놈들이 깃발을 공개하는 걸 고려해봐야 할 것 같아

보닛 아래에는 임시 건설사를 사용하고 있고, 우리가 직접 쓸 수도 있을 것 같습니다.
솔루션의 장점은 생성자가 두 번 호출되지 않는다는 것입니다. 이 경우 속성에 예시와 같이 기본값이 없으면 문제가 발생할 수 있습니다.

각도 1.4 이상(2015-12-06 업데이트):
Angular 팀은 버전 1.4.0에서 이를 직접 지원했습니다(#9425 참조).
오브젝트를 전달하기만 하면 됩니다.$controller기능:

describe('buttons.RemoveButtonCtrl', function () {

  var ctrl, $scope;

  beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();

    ctrl = $controller('xxCtrl', {
      $scope: scope,
    }, {
      label: 'foo'
    });
  }));

  it('should have a label', function () {
    expect(ctrl.label).toBe('foo');
  });
});

블로그 투고도 참조해 주세요.

ES6를 사용한 BindToController 유닛 테스트

ES6를 사용하는 경우 컨트롤러를 직접 Import하여 앵글모크를 사용하지 않고 테스트할 수 있습니다.

지시:

import xxCtrl from './xxCtrl';

class xxDirective {
  constructor() {
    this.bindToController = true;
    this.controller = xxCtrl;
    this.scope = {
      label: '@'
    }
  }
}

app.directive('xx',  new xxDirective());

컨트롤러:

class xxCtrl {
  constructor() {
    this.label = this.label || 'default value';
  }
}

export default xxCtrl;

컨트롤러 테스트:

import xxCtrl from '../xxCtrl';

describe('buttons.RemoveButtonCtrl', function () {

  let ctrl;

  beforeEach(() => {
    xxCtrl.prototype.label = 'foo';
    ctrl = new xxCtrl(stubScope);
  });

  it('should have a label', () => {
    expect(ctrl.label).toBe('foo');
  });

});

자세한 내용은 다음을 참조하십시오. ES6 모듈을 사용한 Angular JS 애플리케이션의 적절한 장치 테스트

제 의견으로는 이 컨트롤러는 단독으로 테스트하는 것이 아닙니다.이 컨트롤러는 단독으로 테스트하는 것은 아닙니다.

app.controller('xxCtrl', function () {
  var ctrl = this;

  // where on earth ctrl.lable comes from???
  ctrl.newLabel = ctrl.label || 'default value';
});

스코프 속성 수신에 의존하는 명령어와 밀접하게 결합되어 있습니다.재사용할 수 없습니다.이 컨트롤러를 보면 이 변수가 어디서 나오는 건지 궁금해요.이는 외부 스코프의 변수를 사용하여 내부적으로 누출 기능을 수행하는 것과 다를 바 없습니다.

function Leaky () {

    ... many lines of code here ...

    // if we are here we are too tired to notice the leakyVariable:
    importantData = process(leakyVariable);

    ... mode code here ...

    return unpredictableResult;
}

이제 누출 기능이 있습니다. 이 함수의 동작은 변수에 따라 매우 예측할 수 없습니다.leakyVariable함수가 호출되는 범위에 관계없이 표시(또는 표시되지 않음)합니다.

당연히 이 기능은 테스트하기에는 악몽입니다.이것은 실제로 좋은 것입니다.아마 개발자가 기능을 모듈러형으로 다시 쓰게 하고 재사용할 수 있게 하는 것입니다.어려운 것은 아닙니다.

function Modular (outsideVariable) {
    ... many lines of code here ...

    // no need to hit our heads against the wall to wonder where the variable comes from:
    importantData = process(outsideVariable);

    ... mode code here ...

    return predictableResult;   
}

누출 문제가 없고 테스트와 재사용이 매우 용이합니다.가 의 좋은 것을 하는 것은$scope더 나은 방법입니다.

app.controller('xxCtrl', function ($scope) {
  $scope.newLabel = $scope.label || 'default value';
});

심플하고, 짧고, 테스트도 간단합니다.또한 부피가 큰 지시 객체 정의도 없습니다.

이면에 있는 최초의 이유는controllerAs구문은 부모로부터 상속받은 누출 범위입니다.그러나 디렉티브의 고립된 범위는 이미 이 문제를 해결했습니다.따라서 벌크 리크 구문을 사용할 이유가 없습니다.

저는 특별히 우아하지는 않지만 적어도 효과가 있는 방법을 찾았습니다(더 좋은 옵션이 있다면 코멘트를 남겨주세요.

이 명령에서 "가져온" 값을 설정하고 컨트롤러 함수를 다시 호출하여 그 기능을 테스트합니다.도우미 "invoke Controller"를 더 드라이하게 만들었습니다.

예를 들어 다음과 같습니다.

describe('buttons.RemoveButtonCtrl', function () {

  var ctrl, $scope;
  beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();
    ctrl = $controller('xxCtrl', {
      $scope: scope,
    });
  }));

  it('should have a label', function () {
    ctrl.label = 'foo'; // set the value

    // call the controller again with all the injected dependencies
    invokeController(ctrl, {
      $scope: scope,
    });

    // test whatever you want
    expect(ctrl.label).toBe('foo');
  });

});


beforeEach(inject(function ($injector) {
  window.invokeController = function (ctrl, locals) {
    locals = locals || {};
    $injector.invoke(ctrl.constructor, ctrl, locals);
  };
}));

언급URL : https://stackoverflow.com/questions/25837774/bindtocontroller-in-unit-tests

반응형