web-dev-qa-db-ja.com

モックコントローラーのインスタンス化Angularディレクティブユニットテスト

Angularディレクティブをユニットテストしていて、ユニットテストで名前付きコントローラーのインスタンス化を何らかの方法でモックまたはスタブしたいと思います。

だから最初に私はいくつかのコードを想定しています...

'use strict';

angular.module('App.Directives.BreadCrumbs', [])

    .directive('kxBreadcrumbs', function () {
        return {
            restrict: 'E',
            controller: 'BreadCrumbsController',
            template:
                '<!-- Breadcrumbs Directive HTML -->' +

                '<ol class="breadcrumb">' +
                '    <li ng-repeat="crumb in crumbPath">' +
                '       <a ng-class="{true: \'disable\', false: \'\'}[crumb.last]" href="{{crumb.href}}" ng-click="updateCrumb(crumb.name)">{{crumb.name}}</a>' +
                '    </li>' +
                '</ol>' +

                '<!-- End of Breadcrumbs Driective HTML -->'
        };

    });

これは、単体テストを行う1つのサンプルディレクティブです。これから取り除く重要なことは、名前付きコントローラーです。

だから私のユニットテストでは

'use strict';

describe('Directives: Breadcrumbs', function () {

    var//iable declarations
        Elm,
        scope,
        $rootScope
    ;

    beforeEach(function () {
        module('App.Directives.BreadCrumbs');
        module('App.Controllers.BreadCrumbs');
        module('App.Constants'); // <--- Comes from the controller dependancy
    });


    beforeEach(inject(function (_$rootScope_, $compile) {
        $rootScope = _$rootScope_;
        scope = $rootScope.$new();

        Elm = angular.element('<kx-breadcrumbs></kx-breadcrumbs>');
        $compile(Elm)(scope);
        scope.$apply();
    }));

    it('Should create the breadcrumbs template', function () {
        scope.crumbPath = [{name: 'home', href: '/'},{name: 'coffee', href: '/coffee'},{name: 'milk', href: '/milk'}];
        scope.$apply();
        var listItem = $(Elm).find('li');
        expect(listItem.length).toBe(3);
        expect($(listItem).text()).toContain('home');
        expect($(listItem).text()).toContain('coffee');
        expect($(listItem).text()).toContain('milk');

    });
});

ディレクティブ、コントローラー、3番目のモジュールの3つのモジュールが含まれていることがわかります。これはコントローラーによって依存関係として参照されるため、これを単体テストに取り込むには、依存関係を取り込む必要があります。さらに悪い場合は、コントローラーからの依存関係を取り込む必要があります。しかし、私はディレクティブユニットテストでコントローラーの機能をユニットテストしていないので、これは冗長であり、モジュールを含めることでコードが肥大化しているように見えます。理想的には、単体テストを行っているモジュールのみを含めたいと思います。

  module('App.Directives.BreadCrumbs');

ではなく(私のポイントをさらに例示するために追加されたモジュール)

  module('App.Directives.BreadCrumbs');
  module('App.Controllers.BreadCrumbs');
  module('App.Constants'); // <--- Comes from the controller dependancy
  module('App.Service.SomeService'); // <--- Comes from the controller dependancy
  module('App.Service.SomeOtherService'); // <--- Comes from the SomeService dependancy

テストコントローラーを単体テストすると、完全に、またはジャスミンスパイを使用して渡されたサービスをモックできます。ディレクティブの単体テストで同じようなことを実行できるので、依存関係の軌跡をたどる必要はありませんか?

26
Sten Muchow

コントローラには$controllerProvider.register()、プロバイダー、ファクトリには$provide.provider()$provide.factory()$provide.service()$provide.value()を使用して、モジュール構成ブロックにモックを作成できます。およびサービス:

JavaScript

beforeEach(function () {
    module('App.Directives.BreadCrumbs', function($provide, $controllerProvider) {
        $controllerProvider.register('BreadCrumbsController', function($scope) {
            // Controller Mock
        });
        $provide.factory('someService', function() {
            // Service/Factory Mock
            return {
                doSomething: function() {}
            }
        });
    });
});

これを行うと、Angularは、モックBreadCrumbsControllerコントローラーをkxBreadcrumbsディレクティブに挿入します。この方法では、実際のコントローラーとその依存関係をに含める必要はありません。単体テスト。

詳細については、Angularの公式ドキュメントを参照してください。

39
Vadim