Как unit test просматривать в ember.js?
Мы находимся в процессе обучения Ember.js. Мы делаем все наши разработки TDD и хотим, чтобы Ember.js не был исключением. У нас есть опыт создания приложений Backbone.js, основанных на тестах, поэтому мы знакомы с тестированием интерфейсного кода с использованием Jasmine или Mocha/Chai.
При определении того, как тестировать представления, мы столкнулись с проблемой, когда шаблон для представления использует оператор #linkTo
. К сожалению, мы не можем найти хорошие примеры тестов и практики. Этот смысл - это наше стремление получить ответы на вопрос о том, как прилично тестировать электронные приложения.
Если посмотреть на test для linkTo в исходном коде Ember.js, мы заметили, что он содержит полную проводку приложения ember для поддержки #linkTo
. Означает ли это, что мы не можем заглушить это поведение при тестировании шаблона?
Как вы создаете тесты для ember-представлений с помощью шаблонов?
Вот gist с нашим тестом и шаблоном, который сделает тестовый проход, и шаблон, который сделает его неудачным.
view_spec.js.coffee
# This test is made with Mocha / Chai,
# With the chai-jquery and chai-changes extensions
describe 'TodoItemsView', ->
beforeEach ->
testSerializer = DS.JSONSerializer.create
primaryKey: -> 'id'
TestAdapter = DS.Adapter.extend
serializer: testSerializer
TestStore = DS.Store.extend
revision: 11
adapter: TestAdapter.create()
TodoItem = DS.Model.extend
title: DS.attr('string')
store = TestStore.create()
@todoItem = store.createRecord TodoItem
title: 'Do something'
@controller = Em.ArrayController.create
content: []
@view = Em.View.create
templateName: 'working_template'
controller: @controller
@controller.pushObject @todoItem
afterEach ->
@view.destroy()
@controller.destroy()
@todoItem.destroy()
describe 'amount of todos', ->
beforeEach ->
# $('#konacha') is a div that gets cleaned between each test
Em.run => @view.appendTo '#konacha'
it 'is shown', ->
$('#konacha .todos-count').should.have.text '1 things to do'
it 'is livebound', ->
expect(=> $('#konacha .todos-count').text()).to.change.from('1 things to do').to('2 things to do').when =>
Em.run =>
extraTodoItem = store.createRecord TodoItem,
title: 'Moar todo'
@controller.pushObject extraTodoItem
broken_template.handlebars
<div class="todos-count"><span class="todos">{{length}}</span> things to do</div>
{{#linkTo "index"}}Home{{/linkTo}}
working_template.handlebars
<div class="todos-count"><span class="todos">{{length}}</span> things to do</div>
Ответы
Ответ 1
Наше решение состояло в том, чтобы существенно загрузить все приложение, но максимально изолировать наших испытуемых. Например,
describe('FooView', function() {
beforeEach(function() {
this.foo = Ember.Object.create();
this.subject = App.FooView.create({ foo: this.foo });
this.subject.append();
});
afterEach(function() {
this.subject && this.subject.remove();
});
it("renders the foo favoriteFood", function() {
this.foo.set('favoriteFood', 'ramen');
Em.run.sync();
expect( this.subject.$().text() ).toMatch( /ramen/ );
});
});
Таким образом, доступны маршрутизатор и другие глобальные переменные, поэтому он не является полной изоляцией, но мы можем легко отправить в два раза предметы, которые ближе к тестируемому объекту.
Если вы действительно хотите изолировать маршрутизатор, помощник linkTo
выглядит как controller.router
, поэтому вы можете сделать
this.router = {
generate: jasmine.createSpy(...)
};
this.subject = App.FooView.create({
controller: { router: this.router },
foo: this.foo
});
Ответ 2
Один из способов справиться с этим - создать заглушку для хелпера linkTo, а затем использовать его перед блоком. Это обойдёт все дополнительные требования реального linkTo (например, маршрутизации) и позволит вам сосредоточиться на содержимом представления. Вот как я это делаю:
// Test helpers
TEST.stubLinkToHelper = function() {
if (!TEST.originalLinkToHelper) {
TEST.originalLinkToHelper = Ember.Handlebars.helpers['link-to'];
}
Ember.Handlebars.helpers['link-to'] = function(route) {
var options = [].slice.call(arguments, -1)[0];
return Ember.Handlebars.helpers.view.call(this, Em.View.extend({
tagName: 'a',
attributeBindings: ['href'],
href: route
}), options);
};
};
TEST.restoreLinkToHelper = function() {
Ember.Handlebars.helpers['link-to'] = TEST.originalLinkToHelper;
TEST.originalLinkToHelper = null;
};
// Foo test
describe('FooView', function() {
before(function() {
TEST.stubLinkToHelper();
});
after(function() {
TEST.restoreLinkToHelper();
});
it('renders the favoriteFood', function() {
var view = App.FooView.create({
context: {
foo: {
favoriteFood: 'ramen'
}
}
});
Em.run(function() {
view.createElement();
});
expect(view.$().text()).to.contain('ramen');
});
});