5

Ember.js アプリケーションでルートを単体テストするための最適なソリューションを探しています。解決策が 2 つ見つかりました。どちらが最適か教えてください。2 つの実装はこちらから入手できます: http://jsbin.com/URaKULa/1/edit

//=====================================
// Source :
//=====================================

App = Em.Application.create({
});

App.UserEditRoute = Ember.Route.extend({
    model: function () {
        // here we tell the route to use its parent model 
        return this.modelFor('user');
    },
    activate: function () {
        this.controllerFor('user').set('editMode', true);
    },
    deactivate: function () {
        this.controllerFor('user').set('editMode', false);
    },
    events: {
        goBack: function () {
            this.transitionTo('user');
        }
    }
});

// defer readiness and set location's router to none in order to stop main application initialization
App.setupForTesting();

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//=====================================
// Test :
//=====================================

//-----------------
// First solution
//-----------------
(function () {
    var originalTemplates;
    var container;
    var Router;
    var router;

    function bootTestApplication() {
        router = container.lookup('router:main');
        Ember.run(TestApp, 'advanceReadiness');
    }

    function handleURL(path) {
        return Ember.run(function () {
            return router.handleURL(path).then(function (value) {
                ok(true, 'url: `' + path + '` was handled');
                return value;
            }, function (reason) {
                ok(false, 'failed to visit:`' + path + '` reason: `' + reason);
                throw reason;
            });
        });
    }


    module('First solution for test routes', {
        setup: function () {
            Em.run(function () {

                // We create a fake test Application to isalate route
                TestApp = Em.Application.create({
                    LOG_TRANSITIONS: true,
                    name: "TestApp",
                    rootElement: "#qunit-fixture"
                });
                // We defer initialization and disabled location
                TestApp.setupForTesting();

                // Save the router
                Router = TestApp.Router;

                // Save a loading route just in case
                TestApp.LoadingRoute = Ember.Route.extend({
                });

                // Save the global TestContainer to lookup  what you want
                container = TestApp.__container__;

                // We save the current ember templates to not impact the other unit test
                // when we will overide a few.
                originalTemplates = Ember.$.extend({}, Ember.TEMPLATES);

                // Override/mock application test for isolation
                Ember.TEMPLATES.application = Ember.Handlebars.compile("{{outlet}}");

                // You can here overide other templates if necessary
            });
        },
        teardown: function () {
            Ember.run(function () {
                // deleting the Test application
                TestApp.destroy();
                TestApp = null;
                // reset old Ember templates
                Ember.TEMPLATES = originalTemplates;
            });
        }
    });

    test('UserEditRoute', function () {
        Router.map(function () {
            this.resource('user', function () {
                this.route('edit');
            });
        });

        // Save the App.UserEditRoute to test into the isolated context
        TestApp.UserEditRoute = App.UserEditRoute;

        // we define a fake UserRoute who will return the expected model of the UserEditRoute
        var expectedUserModel = Em.Object.create({name: 'Model from user route'});
        TestApp.UserRoute = Em.Route.extend({
            model: function () {
                return expectedUserModel;
            }
        });

        // Mock of UserController and UserEditController classes
        TestApp.UserController = Em.Controller.extend({
            editMode: false
        });

        TestApp.UserEditController = Em.Controller;

        // We define an empty template
        Ember.TEMPLATES['user/edit'] = Ember.Handlebars.compile("");

        // Start TestApp application
        bootTestApplication();

        // retrieve UserController instance
        var userCtrl = container.lookup('controller:user');
        var userEditCtrl = container.lookup('controller:userEdit');

        handleURL('/user/edit');
        // assert activate hook behavior and model
        ok(userCtrl.get('editMode'));
        ok(userEditCtrl.get('content', expectedUserModel));

        // reset tested properties
        userCtrl.setProperties({
            editMode: true,
            content: null
        });

        handleURL('/');
        // assert deactivate hook behavior
        ok(!userCtrl.get('editMode'));

        // here we place other test like event handling, or to test others route's hooks
    });
})();


//-----------------
// Second solution
//-----------------
(function(){
  module('Second solution to test routes',{
    setup:function(){
    },
    teardown:function(){
    }
  });

  test('UserEditRoute',function(){
    var container = new Em.Container();
    var expectedUserModel = Em.Object.create({name: 'Model from user route'});

    container.register("controller:user", Em.Controller.extend({
      editMode: false
    }));
    container.register("route:user", Em.Route.extend({
      currentModel:expectedUserModel
    }));

    var userEditRoute = App.UserEditRoute.create({
      router : {
        container:container,
        router:{}
      },
      container:container
    });

    var userCtrl = container.lookup('controller:user');

    equal (userEditRoute.model(), expectedUserModel);
    userEditRoute.activate();
    equal (userCtrl.get('editMode'), true);
    userEditRoute.deactivate();
    equal (userCtrl.get('editMode'), false);
  });
})();

最初のソリューションは、単体テストの Ember.js チームに基づいています (詳細については、https://github.com/emberjs/ember.js/blob/master/packages/ember/tests/routing/basic_test.jsを参照してください) 。このアプローチの長所は、各フックをテストするのではなく、ルートのグローバルな動作をテストできることです。たとえば、「editMode」セットを「setupController」などの別のフックに移動すると、テストは引き続きパスし、正常です。さらに、この方法で、「user/edit」テンプレートにアクションを追加して、イベント処理をテストできます。

このメソッドの大きな短所は、本当に退屈です。テストするコードは、実装よりも 4 倍多くなります...

2 番目のソリューションは、軽量で洗練されていますが、柔軟性に欠けます。依存関係の注入の王様を許可し、UserRoute や UserController などの接続されたすべてのクラスをモックすることを許可するコンテナー クラスを使用しました。このアプローチでは、Emberjs によって内部的にルートで使用されるすべての必要なプロパティとメソッドをモックするように求められます (たとえば、ルーターなど)。

それで、あなたのお気に入りは何ですか?またはあなたは別のものを持っていますか?

4

0 に答える 0