2017-03-24 5 views
2

Angle 1.5コンポーネントをテストするために、DOMをテストする必要がない場合は、$ compileを使用する代わりにngMockの$ componentControllerを使用することをお勧めします。ユニットテストAngular 1.5コンポーネント(ngModelを必要とする)

しかし、私のコンポーネントは、$ componentControllerのためにlocalsに渡す必要があるngModelを使用しますが、プログラムでngModelControllerを取得する方法はありません。それをテストする唯一の方法は、この問題がまだ開いているので、実際に$要素をコンパイルすることです:https://github.com/angular/angular.js/issues/7720

コンポーネントのコントローラを$コンパイルしないでテストする方法はありますか?私はまた、ngModelControllerを自分の行動が多少広がっていると思っているのではなく、実際のテストよりも偽のテストに依存している場合、Angularの新しいバージョンではそれを打ち破ることができます(しかし、 Angular 1が段階的に削除されている問題)。

答えて

1

tl; dr:ソリューションは3番目のコードブロックにあります。

が、プログラムではないという態度でngModelController

を取得する方法はありません。 ;)

あなたはそれをプログラム的に得ることができます。ちょっとしたロータリーです。そうする方法はin the code for ngMock's $componentController service(ここで言い換えれば)です。それをルックアップするために$injector.get('ngModelDirective')を使用して、コントローラ機能はcontrollerプロパティとして、それに添付されます。

this.$get = ['$controller','$injector', '$rootScope', function($controller, $injector, $rootScope) { 
    return function $componentController(componentName, locals, bindings, ident) { 
     // get all directives associated to the component name 
     var directives = $injector.get(componentName + 'Directive'); 
     // look for those directives that are components 
     var candidateDirectives = directives.filter(function(directiveInfo) { 
      // ... 
     }); 

     // ... 

     // get the info of the component 
     var directiveInfo = candidateDirectives[0]; 
     // create a scope if needed 
     locals = locals || {}; 
     locals.$scope = locals.$scope || $rootScope.$new(true); 
     return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs); 
    }; 
}]; 

あなたがそれをインスタンス化するとき$element$attrsためngModelControllerの地元の人々を提供する必要がありますが。 The test spec for ngModel demonstrates exactly how to do this in its beforeEach call:だから

beforeEach(inject(function($rootScope, $controller) { 
    var attrs = {name: 'testAlias', ngModel: 'value'}; 


    parentFormCtrl = { 
     $$setPending: jasmine.createSpy('$$setPending'), 
     $setValidity: jasmine.createSpy('$setValidity'), 
     $setDirty: jasmine.createSpy('$setDirty'), 
     $$clearControlValidity: noop 
    }; 


    element = jqLite('<form><input></form>'); 


    scope = $rootScope; 
    ngModelAccessor = jasmine.createSpy('ngModel accessor'); 
    ctrl = $controller(NgModelController, { 
     $scope: scope, 
     $element: element.find('input'), 
     $attrs: attrs 
    }); 


    //Assign the mocked parentFormCtrl to the model controller 
    ctrl.$$parentForm = parentFormCtrl; 
})); 

、私たちが必要とするものに、私たちはこのような仕様を取得することを適応:

describe('Unit: myComponent', function() { 
    var $componentController, 
     $controller, 
     $injector, 
     $rootScope; 

    beforeEach(inject(function (_$componentController_, _$controller_, _$injector_, _$rootScope_) { 
     $componentController = _$componentController_; 
     $controller = _$controller_; 
     $injector = _$injector_; 
     $rootScope = _$rootScope_; 
    })); 

    it('should update its ngModel value accordingly', function() { 
     var ngModelController, 
      locals 
      ngModelInstance, 
      $ctrl; 

     locals = { 
      $scope: $rootScope.$new(), 
      //think this could be any element, honestly, but matching the component looks better 
      $element: angular.element('<my-component></my-component>'), 
      //the value of $attrs.ngModel is exactly what you'd put for ng-model in a template 
      $attrs: { ngModel: 'value' } 
     }; 
     locals.$scope.value = null; //this is what'd get passed to ng-model in templates 
     ngModelController = $injector.get('ngModelDirective')[0].controller; 

     ngModelInstance = $controller(ngModelController, locals); 

     $ctrl = $componentController('myComponent', null, { ngModel: ngModelInstance }); 

     $ctrl.doAThingToUpdateTheModel(); 
     scope.$digest(); 

     //Check against both the scope value and the $modelValue, use toBe and toEqual as needed. 
     expect(ngModelInstance.$modelValue).toBe('some expected value goes here'); 
     expect(locals.$scope.value).toBe('some expected value goes here'); 
    }); 
}); 

補遺を:あなたはまた、代わりにbeforeEachngModelDirectiveを注入して設定することで、さらにそれを簡素化することができますあなたが$controllerのようなサービスと同じように、コントローラ機能を含むdescribeブロックのvar。

describe('...', function() { 
    var ngModelController; 

    beforeEach(inject(function(_ngModelDirective_) { 
     ngModelController = _ngModelDirective_[0].controller; 
    })); 
}); 
関連する問題