Pat Maddox, B.D.D.M.F.

RSpec my authority! I just smashed a bug

Backbone.js Testing Pattern - Describe a View's First Render

While testing my backbone.js views, I’ve come up with a few conventions to keep my tests neatly organized. The first is to have a single describe block to specify what the view looks like when it first renders. This tests the view’s rendering logic and establishes the foundation for the rest of my examples. Here’s a brief example. I have a view which renders a person’s name and adds a CSS class if the person is a minor. Pretty simple. There are three things I’m interested in when testing the first render:

  • show the name
  • no CSS class if person is over 18
  • special CSS class if person is under 18

Here’s the view spec:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
describe PersonView, ->
  describe "first render", ->
    beforeEach ->
      @model = new Person(name: 'Joe')
      @view = new PersonView(model: @model)

    it "shows the name in an h2", ->
      expect($(@view.render().el).find('h2').text()).toEqual('Joe')

    it "does not add the 'minor' class to the h2 if person is not a minor", ->
      @model.set age: 18
      expect($(@view.render().el).find('h2').hasClass('minor')).toBe(false)

    it "adds the 'minor' class to the h2 if person is a minor", ->
      @model.set age: 17
      expect($(@view.render().el).find('h2').hasClass('minor')).toBe(true)

And now the view that makes this work:

1
2
3
4
5
6
7
8
class window.PersonView extends Backbone.View
  initialize: ->
    @template = _.template($('person-view-template').html())

  render: ->
    $(@el).html(@template(model: @model))
    $(@el).find('h2').addClass('minor') if @model.isMinor()
    this

It uses this super simple template:

1
2
3
4
5
<script type="text/template" id="person-template">
  <div class="person">
    <h2> {{ model.name() }} </h2>
  </div>
</script>

If the view logic is complex I may separate the conditions into separate describe blocks, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
describe PersonView, ->
  describe "first render", ->
    beforeEach ->
      @model = new Person(name: 'Joe')
      @view = new PersonView(model: @model)

    describe "person is not a minor", ->
      beforeEach ->
        @model.set age: 18

      it "shows the name in an h2", ->
        expect($(@view.render().el).find('h2').text()).toEqual('Joe')

      it "does not add the 'minor' class to the h2", ->
        expect($(@view.render().el).find('h2').hasClass('minor')).toBe(false)

    describe "person is a minor", ->
      beforeEach ->
        @model.set age: 17

      it "shows the name in an h2", ->
        expect($(@view.render().el).find('h2').text()).toEqual('Joe')

      it "adds the 'minor' class to the h2", ->
        expect($(@view.render().el).find('h2').hasClass('minor')).toBe(true)

With these tests in place, I can start testing the events. Details on that will be in a future post.

Join the discussion on Google+