Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test views in ember.js?

We are in the process of learning Ember.js. We do all our development TDD, and want Ember.js to be no exception. We have experience building Backbone.js apps test-driven, so we are familiar with testing front-end code using Jasmine or Mocha/Chai.

When figuring out how to test views, we ran into a problem when the template for the view uses has a #linkTo statement. Unfortunately we are unable to find good test examples and practices. This gist is our quest to get answers how to decently unit-test ember applications.

When looking at the test for linkTo in Ember.js source code, we noticed it contains a full wiring of an ember app to support #linkTo. Does this mean we cannot stub this behaviour when testing a template?

How do you create tests for ember views using template renders?

Here is a gist with our test and a template that will make the test pass, and a template that will make it fail.

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>
like image 248
THAiSi Avatar asked Feb 15 '13 21:02

THAiSi


People also ask

Can you unit test UI?

Unit Tests Don't Test The UI Most application users spend all of their time interacting with your program through it's UI, it's user interface. Unit tests are back-end tests, they can check that a calculation is correct, but they can't verify that the results display correctly to your user.

How unit test is done in JavaScript?

JavaScript Unit Testing is a method where JavaScript test code is written for a web page or web application module. It is then combined with HTML as an inline event handler and executed in the browser to test if all functionalities are working as desired. These unit tests are then organized in the test suite.

What is unit test assertions?

The assert section ensures that the code behaves as expected. Assertions replace us humans in checking that the software does what it should. They express requirements that the unit under test is expected to meet. Now, often one can write slightly different assertions to capture a given requirement.


1 Answers

Our solution has been to essentially load the whole application, but isolate our test subjects as much as possible. For example,

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/ );
  });
});

That is, the router and other globals are available, so it's not complete isolation, but we can easily send in doubles for things closer to the object under test.

If you really want to isolate the router, the linkTo helper looks it up as controller.router, so you could do

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

this.subject = App.FooView.create({
  controller: { router: this.router },
  foo: this.foo
});
like image 93
James A. Rosen Avatar answered Oct 20 '22 18:10

James A. Rosen