2013-02-15 12 views
14

Jesteśmy w trakcie uczenia się Ember.js. Robimy wszystkie nasze TDD rozwoju i chcemy, aby Ember.js nie był wyjątkiem. Mamy doświadczenie w budowaniu aplikacji opartych na testach Backbone.js, więc znamy testowanie kodu front-end przy użyciu Jasmine lub Mocha/Chai.Jak wyświetlić widoki testu urządzenia w pliku ember.js?

Kiedy zastanawialiśmy się, jak przetestować widoki, natrafiliśmy na problem, gdy szablon widoku używa instrukcji #linkTo. Niestety nie jesteśmy w stanie znaleźć dobrych przykładów testów i praktyk. To jest nasze zadanie, aby uzyskać odpowiedzi na pytanie, jak przyzwoicie testować aplikacje ember.

Po obejrzeniu test for linkTo in Ember.js source code, zauważyliśmy, że zawiera ono pełne okablowanie aplikacji ember do obsługi #linkTo. Czy to oznacza, że ​​nie możemy zablokować tego zachowania podczas testowania szablonu?

Jak utworzyć testy dla widoków embera za pomocą renderowania szablonów?

Oto a gist z naszym testem i szablonem, który sprawi, że test przejdzie i szablon, który sprawi, że się nie powiedzie.

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

Zasadniczo będziesz mieć problemy z tworzeniem instancji w odosobnieniu. Ember chce, aby Twoja aplikacja działała. To nie jest odpowiedź, ale ogólnie wolę testy integracyjne (zobacz [ten film wideo] (http://www.youtube.com/watch?v=heK78M6Q99Q)) w porównaniu z testami jednostkowymi. Widziałem, jak ludzie próbowali testować poglądy w odosobnieniu, a nawet kiedy to działa, nie wydaje się dobrą strategią. Zobacz http://www.slideshare.net/jo_liss/testing-ember-apps/27, aby poznać niektóre z racjonalnych przesłanek. –

Odpowiedz

9

Nasze rozwiązanie polegało na zasadniczo załadowaniu całej aplikacji, ale izolowanie naszych obiektów testowych w jak największym stopniu. Na przykład,

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's favoriteFood", function() { 
    this.foo.set('favoriteFood', 'ramen'); 
    Em.run.sync(); 
    expect(this.subject.$().text()).toMatch(/ramen/); 
    }); 
}); 

Oznacza to, że router i inne globalne są dostępne, więc nie jest to kompletny izolacja, ale łatwo możemy wysłać w deblu rzeczy bliżej do obiektu badanego.

Jeśli naprawdę chcesz, aby wyizolować do rutera, linkTo pomocnik szuka go jako controller.router, więc można zrobić

this.router = { 
    generate: jasmine.createSpy(...) 
}; 

this.subject = App.FooView.create({ 
    controller: { router: this.router }, 
    foo: this.foo 
}); 
1

Jeden sposób można obsłużyć to stworzyć zalążek dla pomocnika linkTo a następnie użyj go w bloku przed. To obejmie wszystkie dodatkowe wymagania rzeczywistego linkTo (np. Routing) i pozwoli ci skupić się na zawartości widoku. Oto, jak to robię:

// 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'); 
    }); 
}); 
Powiązane problemy