5

我正在寻找对 Ember.js 应用程序中的路由进行单元测试的最佳解决方案。我找到了两个解决方案,我想让你告诉我什么是最适合你的。这两个实现在这里可用: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”之类的另一个钩子中,则测试继续通过并且是正常的。此外,通过这种方式,我可以通过在“用户/编辑”模板中添加一个操作来测试事件处理。

最大的缺点是,这种方法真的很无聊,我们要测试的代码是实现的四倍......

第二种解决方案更轻巧、更优雅,但灵活性较差。我们使用了容器类,它允许大量注入依赖,并允许模拟所有连接的类,如 UserRoute 或 UserController。这种方法要求您模拟 Emberjs 内部路由使用的所有必要属性和方法(例如路由器)。

那么你最喜欢什么?或者你有另一个?

4

0 回答 0