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 によって内部的にルートで使用されるすべての必要なプロパティとメソッドをモックするように求められます (たとえば、ルーターなど)。
それで、あなたのお気に入りは何ですか?またはあなたは別のものを持っていますか?