SlideShare ist ein Scribd-Unternehmen logo
1 von 64
TESTING RICH *SCRIPT
APPLICATIONS WITH RAILS
         @markbates
Finished in 14.41041 seconds
108 examples, 0 failures
Video demo of a Todo list application goes here.
app/models/todo.rb
class Todo < ActiveRecord::Base

  validates :body, presence: true

  attr_accessible :body, :completed

end
spec/models/todo_spec.rb
require 'spec_helper'

describe Todo do

  it "requires a body" do
    todo = Todo.new
    todo.should_not be_valid
    todo.errors[:body].should include("can't be blank")
    todo.body = "Do something"
    todo.should be_valid
  end

end
app/controllers/todos_controller.rb
class TodosController < ApplicationController
  respond_to :html, :json

  def index
    respond_to do |format|
      format.html {}
      format.json do
        @todos = Todo.order("created_at asc")
        respond_with @todos
      end
    end
  end

  def show
    @todo = Todo.find(params[:id])
    respond_with @todo
  end

  def create
    @todo = Todo.create(params[:todo])
    respond_with @todo
  end

  def update
    @todo = Todo.find(params[:id])
    @todo.update_attributes(params[:todo])
    respond_with @todo
  end

  def destroy
    @todo = Todo.find(params[:id])
    @todo.destroy
    respond_with @todo
  end

end
spec/controllers/todos_controller_spec.rb
require 'spec_helper'
                                                                                   it "responds with errors" do
                                                                                       expect {
describe TodosController do
                                                                                         post :create, todo: {}, format: 'json'

  let(:todo) { Factory(:todo) }
                                                                                         response.should_not be_successful
                                                                                         json = decode_json(response.body)
  describe 'index' do
                                                                                         json.errors.should have(1).error
                                                                                         json.errors.body.should include("can't be blank")
    context "HTML" do
                                                                                       }.to_not change(Todo, :count)
                                                                                   end
        it "renders the HTML page" do
          get :index
                                                                                 end

          response.should render_template(:index)
                                                                             end
          assigns(:todos).should be_nil
        end
                                                                             describe 'update' do

    end
                                                                                 context "JSON" do

    context "JSON" do
                                                                                   it "updates a todo" do
                                                                                     put :update, id: todo.id, todo: {body: "do something else"}, format: 'json'
        it "returns JSON for the todos" do
          get :index, format: "json"
                                                                                       response.should be_successful
                                                                                       todo.reload
          response.should_not render_template(:index)
                                                                                       todo.body.should eql "do something else"
          assigns(:todos).should_not be_nil
                                                                                   end
        end

                                                                                   it "responds with errors" do
    end
                                                                                     put :update, id: todo.id, todo: {body: ""}, format: 'json'

  end
                                                                                       response.should_not be_successful
                                                                                       json = decode_json(response.body)
  describe 'show' do
                                                                                       json.errors.should have(1).error
                                                                                     json.errors.body.should include("can't be blank")
    context "JSON" do
                                                                                   end

        it "returns the todo" do
                                                                                 end
          get :show, id: todo.id, format: 'json'

                                                                             end
          response.should be_successful
          response.body.should eql todo.to_json
                                                                             describe 'destroy' do
        end

                                                                                 context "JSON" do
    end

                                                                                   it "destroys the todo" do
  end
                                                                                       todo.should_not be_nil
                                                                                       expect {
  describe 'create' do
                                                                                         delete :destroy, id: todo.id, format: 'JSON'
                                                                                       }.to change(Todo, :count).by(-1)
    context "JSON" do
                                                                                   end

        it "creates a new todo" do
                                                                                 end
          expect {
              post :create, todo: {body: "do something"}, format: 'json'
                                                                             end

            response.should be_successful
                                                                           end
          }.to change(Todo, :count).by(1)
        end
app/views/todos/index.html.erb
<form class='form-horizontal' id='todo_form'></form>

<ul id='todos' class="unstyled"></ul>

<script>
  $(function() {
     new OMG.Views.TodosApp();
  })
</script>
SO WHERE’S THE CODE?
app/assets/javascripts/views/todo_view.js.coffee
class OMG.Views.TodoView extends OMG.Views.BaseView

  tagName: 'li'
  template: JST['todos/_todo']

  events:
    'change [name=completed]': 'completedChecked'
    'click .delete': 'deleteClicked'

  initialize: ->
    @model.on "change", @render
    @render()

  render: =>
    $(@el).html(@template(todo: @model))
    if @model.get("completed") is true
      @$(".todo-body").addClass("completed")
      @$("[name=completed]").attr("checked", true)
    return @

  completedChecked: (e) =>
    @model.save(completed: $(e.target).attr("checked")?)

  deleteClicked: (e) =>
    e?.preventDefault()
    if confirm("Are you sure?")
      @model.destroy()
      $(@el).remove()
HOW DO WE TEST THIS?
CAPYBARA?
X
CAPYBARA?
https://github.com/pivotal/jasmine
 It’s like RSpec for JavaScript*
           * or CoffeeScript
JavaScript example:
describe('panda',function(){
  it('is happy',function(){
    expect(panda).toBe('happy');
  });
});




                        CoffeeScript example:
describe 'panda', ->
  it 'is happy', ->
    expect(panda).toBe('happy')
MATCHERS
•   expect(x).toEqual(y)         •   expect(x).toBeFalsy()

•   expect(x).toBe(y)            •   expect(x).toContain(y)

•   expect(x).toMatch(pattern)   •   expect(x).toBeLessThan(y)

•   expect(x).toBeDefined()       •   expect(x).toBeGreaterThan(y)

•   expect(x).toBeUndefined()     •   expect(function()
                                     {fn();}).toThrow(e)
•   expect(x).toBeNull()

•   expect(x).toBeTruthy()
NOT-MATCHERS
•   expect(x).not.toEqual(y)         •   expect(x).not.toBeFalsy()

•   expect(x).not.toBe(y)            •   expect(x).not.toContain(y)

•   expect(x).not.toMatch(pattern)   •   expect(x).not.toBeLessThan(y)

•   expect(x).not.toBeDefined()       •   expect(x).not.toBeGreaterThan(y)

•   expect(x).not.toBeUndefined()     •   expect(function()
                                         {fn();}).not.toThrow(e)
•   expect(x).not.toBeNull()

•   expect(x).not.toBeTruthy()
JASMINE WITH RAILS

• gem   'evergreen', '>= 1.0.0', :require => 'evergreen/rails'

• gem   'capybara-webkit'
rake spec:javascripts
LET’S WRITE A TEST!
spec/javascripts/spec_helper.coffee
# Require the appropriate asset-pipeline files:
require '/assets/application.js'

# Any other testing specific code here...
# Custom matchers, etc....

beforeEach ->
  @_page = $('#test')
app/assets/javascript/greeter.js.coffee
class @Greeter

  constructor: (@name) ->

  greet: ->
    "Hi #{@name}"
spec/javascripts/greeter_spec.coffee
describe "Greeter", ->

  beforeEach ->
    @greeter = new Greeter("Mark")

  it "greets someone", ->
    expect(@greeter.greet()).toEqual("Hi Mark")
jasmine-jquery
https://github.com/velesin/jasmine-jquery
config/environments/development.rb
OMG::Application.configure do

  # ...

  config.assets.paths << Rails.root.join("spec", "javascripts", "support")

end
MATCHERS
•   toBe(jQuerySelector)        •   toExist()

•   toBeChecked()               •   toHaveAttr(attributeName,
                                    attributeValue)
•   toBeEmpty()
                                •   toHaveBeenTriggeredOn(selector)
•   toBeHidden()
                                •   toHaveClass(className)
•   toBeSelected()
                                •   toHaveData(key, value)
•   toBeVisible()
                                •   toHaveHtml(string)
•   toContain(jQuerySelector)
spec/javascripts/spec_helper.coffee
# Require the appropriate asset-pipeline files:
require '/assets/application.js'
require '/assets/jasmine.jquery.js'

# Any other testing specific code here...
# Custom matchers, etc....

beforeEach ->
  @_page = $('#test')
app/assets/javascripts/views/todo_view.js.coffee
class OMG.Views.TodoView extends OMG.Views.BaseView

  tagName: 'li'
  template: JST['todos/_todo']

  events:
    'change [name=completed]': 'completedChecked'
    'click .delete': 'deleteClicked'

  initialize: ->
    @model.on "change", @render
    @render()

  render: =>
    $(@el).html(@template(todo: @model))
    if @model.get("completed") is true
      @$(".todo-body").addClass("completed")
      @$("[name=completed]").attr("checked", true)
    return @

  completedChecked: (e) =>
    @model.save(completed: $(e.target).attr("checked")?)

  deleteClicked: (e) =>
    e?.preventDefault()
    if confirm("Are you sure?")
      @model.destroy()
      $(@el).remove()
spec/javascripts/views/todos/todo_view_spec.coffee
describe "OMG.Views.TodoView", ->

  beforeEach ->
    @collection = new OMG.Collections.Todos()
    @model = new OMG.Models.Todo(id: 1, body: "Do something!", completed: false)
    @view = new OMG.Views.TodoView(model: @model, collection: @collection)
    @_page.html(@view.el)
spec/javascripts/views/todos/todo_view_spec.coffee
describe "model bindings", ->

  it "re-renders on change", ->
    expect($('.todo-body')).toHaveText("Do something!")
    @model.set(body: "Do something else!")
    expect($('.todo-body')).toHaveText("Do something else!")
spec/javascripts/views/todos/todo_view_spec.coffee
describe "displaying of todos", ->

  it "contains the body of the todo", ->
    expect($('.todo-body')).toHaveText("Do something!")

  it "is not marked as completed", ->
    expect($('[name=completed]')).not.toBeChecked()
    expect($('.todo-body')).not.toHaveClass("completed")

  describe "completed todos", ->

    beforeEach ->
      @model.set(completed: true)

    it "is marked as completed", ->
      expect($('[name=completed]')).toBeChecked()
      expect($('.todo-body')).toHaveClass("completed")
spec/javascripts/views/todos/todo_view_spec.coffee
describe "checking the completed checkbox", ->

  beforeEach ->
    expect($('[name=completed]')).not.toBeChecked()
    $('[name=completed]').click()

  it "marks it as completed", ->
    expect($('[name=completed]')).toBeChecked()
    expect($('.todo-body')).toHaveClass("completed")

describe "unchecking the completed checkbox", ->

  beforeEach ->
    @model.set(completed: true)
    expect($('[name=completed]')).toBeChecked()
    $('[name=completed]').click()

  it "marks it as not completed", ->
    expect($('[name=completed]')).not.toBeChecked()
    expect($('.todo-body')).not.toHaveClass("completed")
app/assets/javascripts/todos/todo_view.coffee
class OMG.Views.TodoView extends OMG.Views.BaseView

  # ...

  deleteClicked: (e) =>
    e?.preventDefault()
    if confirm("Are you sure?")
      @model.destroy()
      $(@el).remove()
MATCHERS
•   expect(x).toHaveBeenCalled()

•   expect(x).toHaveBeenCalledWith(arguments)

•   expect(x).not.toHaveBeenCalled()

•   expect(x).not.toHaveBeenCalledWith(arguments)
TRAINING SPIES
•   spyOn(x, 'method').andCallThrough()

•   spyOn(x, 'method').andReturn(arguments)

•   spyOn(x, 'method').andThrow(exception)

•   spyOn(x, 'method').andCallFake(function)
spec/javascripts/views/todos/todo_view_spec.coffee
describe "clicking the delete button", ->

  describe "if confirmed", ->

    beforeEach ->
      spyOn(window, "confirm").andReturn(true)

    it "will remove the todo from the page", ->
      expect(@_page.html()).toContain($(@view.el).html())
      $(".delete").click()
      expect(@_page.html()).not.toContain($(@view.el).html())

  describe "if not confirmed", ->

    beforeEach ->
      spyOn(window, "confirm").andReturn(false)

    it "will not remove the todo from the page", ->
      expect(@_page.html()).toContain($(@view.el).html())
      $(".delete").click()
      expect(@_page.html()).toContain($(@view.el).html())
CUSTOM MATCHERS
spec/javascripts/spec_helper.coffee
# Require the appropriate asset-pipeline files:
require '/assets/application.js'
require '/assets/jasmine.jquery.js'

# Any other testing specific code here...
# Custom matchers, etc....
beforeEach ->
  @_page = $('#test')

  @addMatchers
    toContainView: (view) ->
      actual_html = $(@actual).html()
      view_html = $(view.el).html()
      @message = ->
        "Expected #{actual_html} to contain #{view_html}"
      return @env.contains_(actual_html, view_html)
spec/javascripts/views/todos/todo_view_spec.coffee
describe "clicking the delete button", ->

  describe "if confirmed", ->

    beforeEach ->
      spyOn(window, "confirm").andReturn(true)

    it "will remove the todo from the page", ->
      expect(@_page).toContainView(@view)
      $(".delete").click()
      expect(@_page).not.toContainView(@view)

  describe "if not confirmed", ->

    beforeEach ->
      spyOn(window, "confirm").andReturn(false)

    it "will not remove the todo from the page", ->
      expect(@_page).toContainView(@view)
      $(".delete").click()
      expect(@_page).toContainView(@view)
WHAT ABOUT AJAX
  REQUESTS?
app/assets/javascripts/views/todos/todo_list_view.js.coffee
class OMG.Views.TodosListView extends OMG.Views.BaseView

  el: "#todos"

  initialize: ->
    @collection.on "reset", @render
    @collection.on "add", @renderTodo
    @collection.fetch()

  render: =>
    $(@el).html("")
    @collection.forEach (todo) =>
      @renderTodo(todo)

  renderTodo: (todo) =>
    view = new OMG.Views.TodoView(model: todo, collection: @collection)
    $(@el).prepend(view.el)
spec/javascripts/views/todos/todo_list_view_spec.coffee
describe "OMG.Views.TodosListView", ->

  beforeEach ->
    @_page.html("<ul id='todos'></ul>")
    @collection = new OMG.Collections.Todos()
    @view = new OMG.Views.TodosListView(collection: @collection)

  it "fetches the collection", ->
    expect(@collection.length).toEqual(2)

  it "renders the todos from the collection", ->
    expect($(@view.el).html()).toMatch("Do something!")
    expect($(@view.el).html()).toMatch("Do something else!")

  it "renders new todos added to the collection", ->
    @collection.add(new OMG.Models.Todo(body: "Do another thing!"))
    expect($(@view.el).html()).toMatch("Do another thing!")
rake spec:javascripts
...FF...........

  Failed: OMG.Views.TodosListView fetches the collection.
    Expected 0 to equal 2.
    in :

  Failed: OMG.Views.TodosListView renders the todos from the collection.
    Expected '' to match 'Do something!'.
    in :

Finished in 3.12 seconds
16 examples, 2 failures
jasmine-ajax
https://github.com/pivotal/jasmine-ajax
spec/javascripts/spec_helper.coffee
# Require the appropriate asset-pipeline files:
require '/assets/application.js'
require '/assets/jasmine.jquery.js'
require '/assets/mock-ajax.js'
require '/assets/mock_responses.js'

# Any other testing specific code here...
# Custom matchers, etc....
beforeEach ->
  @_page = $('#test')

  @addMatchers
    toContainView: (view) ->
      actual_html = $(@actual).html()
      view_html = $(view.el).html()
      @message = ->
        "Expected #{actual_html} to contain #{view_html}"
      return @env.contains_(actual_html, view_html)
1. DEFINE TEST RESPONSE(S)
spec/javascripts/support/mock_responses.coffee
@Mocks =
  todos:
    todo_1:
      status: 200
      responseText: '{"body":"Do something!","completed":false,"id":1}'
    collection:
      status: 200
      responseText: '''
         [
           {"body":"Do something!","completed":false,"id":1},
           {"body":"Do something else!","completed":false,"id":2}
         ]'''
2. INSTALL THE MOCK(S)
spec/javascripts/views/todos/todo_list_view_spec.coffee
describe "OMG.Views.TodosListView", ->

  beforeEach ->
    jasmine.Ajax.useMock()
    @_page.html("<ul id='todos'></ul>")
    @collection = new OMG.Collections.Todos()
    @view = new OMG.Views.TodosListView(collection: @collection)

  it "fetches the collection", ->
    expect(@collection.length).toEqual(2)

  it "renders the todos from the collection", ->
    expect($(@view.el).html()).toMatch("Do something!")
    expect($(@view.el).html()).toMatch("Do something else!")

  it "renders new todos added to the collection", ->
    @collection.add(new OMG.Models.Todo(body: "Do another thing!"))
    expect($(@view.el).html()).toMatch("Do another thing!")
3. SET RESPONSES(S)
spec/javascripts/views/todos/todo_list_view_spec.coffee
describe "OMG.Views.TodosListView", ->

  beforeEach ->
    jasmine.Ajax.useMock()
    @_page.html("<ul id='todos'></ul>")
    @collection = new OMG.Collections.Todos()
    @view = new OMG.Views.TodosListView(collection: @collection)
    request = mostRecentAjaxRequest()
    request.response(Mocks.todos.collection)

  it "fetches the collection", ->
    expect(@collection.length).toEqual(2)

  it "renders the todos from the collection", ->
    expect($(@view.el).html()).toMatch("Do something!")
    expect($(@view.el).html()).toMatch("Do something else!")

  it "renders new todos added to the collection", ->
    @collection.add(new OMG.Models.Todo(body: "Do another thing!"))
    expect($(@view.el).html()).toMatch("Do another thing!")
rake spec:javascripts


................

Finished in 2.94 seconds
16 examples, 0 failures
THANKS
                                         @markbates
•   jasmine
    https://github.com/pivotal/jasmine

•   jasmine-jquery
    https://github.com/velesin/jasmine-jquery/

•   jasmine-ajax
    https://github.com/pivotal/jasmine-ajax

•   evergreen
    https://github.com/jnicklas/evergreen

•   capybara-webkit
    https://github.com/thoughtbot/capybara-webkit

•   Programming in CoffeeScript
    http://books.markbates.com

Weitere ähnliche Inhalte

Was ist angesagt?

運用Closure Compiler 打造高品質的JavaScript
運用Closure Compiler 打造高品質的JavaScript運用Closure Compiler 打造高品質的JavaScript
運用Closure Compiler 打造高品質的JavaScripttaobao.com
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mockskenbot
 
Testing Backbone applications with Jasmine
Testing Backbone applications with JasmineTesting Backbone applications with Jasmine
Testing Backbone applications with JasmineLeon van der Grient
 
descriptive programming
descriptive programmingdescriptive programming
descriptive programmingAnand Dhana
 
Why Every Tester Should Learn Ruby
Why Every Tester Should Learn RubyWhy Every Tester Should Learn Ruby
Why Every Tester Should Learn RubyRaimonds Simanovskis
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 
Stay with React.js in 2020
Stay with React.js in 2020Stay with React.js in 2020
Stay with React.js in 2020Jerry Liao
 
Vbscript reference
Vbscript referenceVbscript reference
Vbscript referenceRahul Ranjan
 
Testing your javascript code with jasmine
Testing your javascript code with jasmineTesting your javascript code with jasmine
Testing your javascript code with jasmineRubyc Slides
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScriptAndrew Dupont
 
Building a Pluggable Plugin
Building a Pluggable PluginBuilding a Pluggable Plugin
Building a Pluggable PluginBrandon Dove
 
Conf soat tests_unitaires_Mockito_jUnit_170113
Conf soat tests_unitaires_Mockito_jUnit_170113Conf soat tests_unitaires_Mockito_jUnit_170113
Conf soat tests_unitaires_Mockito_jUnit_170113SOAT
 
2 introduction toentitybeans
2 introduction toentitybeans2 introduction toentitybeans
2 introduction toentitybeansashishkirpan
 
Wait queue
Wait queueWait queue
Wait queueRoy Lee
 
Javascript basics
Javascript basicsJavascript basics
Javascript basicsFin Chen
 
Clean code in JavaScript
Clean code in JavaScriptClean code in JavaScript
Clean code in JavaScriptMathieu Breton
 

Was ist angesagt? (20)

運用Closure Compiler 打造高品質的JavaScript
運用Closure Compiler 打造高品質的JavaScript運用Closure Compiler 打造高品質的JavaScript
運用Closure Compiler 打造高品質的JavaScript
 
Rails on Oracle 2011
Rails on Oracle 2011Rails on Oracle 2011
Rails on Oracle 2011
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mocks
 
Testing Backbone applications with Jasmine
Testing Backbone applications with JasmineTesting Backbone applications with Jasmine
Testing Backbone applications with Jasmine
 
descriptive programming
descriptive programmingdescriptive programming
descriptive programming
 
Why Every Tester Should Learn Ruby
Why Every Tester Should Learn RubyWhy Every Tester Should Learn Ruby
Why Every Tester Should Learn Ruby
 
Java and xml
Java and xmlJava and xml
Java and xml
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Stay with React.js in 2020
Stay with React.js in 2020Stay with React.js in 2020
Stay with React.js in 2020
 
Scala in practice
Scala in practiceScala in practice
Scala in practice
 
Vbscript reference
Vbscript referenceVbscript reference
Vbscript reference
 
Testing your javascript code with jasmine
Testing your javascript code with jasmineTesting your javascript code with jasmine
Testing your javascript code with jasmine
 
SOLID PRINCIPLES
SOLID PRINCIPLESSOLID PRINCIPLES
SOLID PRINCIPLES
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
Building a Pluggable Plugin
Building a Pluggable PluginBuilding a Pluggable Plugin
Building a Pluggable Plugin
 
Conf soat tests_unitaires_Mockito_jUnit_170113
Conf soat tests_unitaires_Mockito_jUnit_170113Conf soat tests_unitaires_Mockito_jUnit_170113
Conf soat tests_unitaires_Mockito_jUnit_170113
 
2 introduction toentitybeans
2 introduction toentitybeans2 introduction toentitybeans
2 introduction toentitybeans
 
Wait queue
Wait queueWait queue
Wait queue
 
Javascript basics
Javascript basicsJavascript basics
Javascript basics
 
Clean code in JavaScript
Clean code in JavaScriptClean code in JavaScript
Clean code in JavaScript
 

Ähnlich wie Testing Rich Client Side Apps with Jasmine

Javascript: the important bits
Javascript: the important bitsJavascript: the important bits
Javascript: the important bitsChris Saylor
 
Rails2 Pr
Rails2 PrRails2 Pr
Rails2 Prxibbar
 
Introduction to Kotlin
Introduction to KotlinIntroduction to Kotlin
Introduction to KotlinPatrick Yin
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on AndroidSven Haiges
 
Intro to Scala.js - Scala UG Cologne
Intro to Scala.js - Scala UG CologneIntro to Scala.js - Scala UG Cologne
Intro to Scala.js - Scala UG CologneMarius Soutier
 
EPiServer report generation
EPiServer report generationEPiServer report generation
EPiServer report generationPaul Graham
 
Rails 2010 Workshop
Rails 2010 WorkshopRails 2010 Workshop
Rails 2010 Workshopdtsadok
 
Mongoskin - Guilin
Mongoskin - GuilinMongoskin - Guilin
Mongoskin - GuilinJackson Tian
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to NodejsGabriele Lana
 
Sprout core and performance
Sprout core and performanceSprout core and performance
Sprout core and performanceYehuda Katz
 
Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript using CoffeeScript, Backbone.js and JasmineRails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript using CoffeeScript, Backbone.js and JasmineRaimonds Simanovskis
 
Introduction to ReasonML
Introduction to ReasonMLIntroduction to ReasonML
Introduction to ReasonMLRiza Fahmi
 
浜松Rails3道場 其の参 Controller編
浜松Rails3道場 其の参 Controller編浜松Rails3道場 其の参 Controller編
浜松Rails3道場 其の参 Controller編Masakuni Kato
 

Ähnlich wie Testing Rich Client Side Apps with Jasmine (20)

Javascript: the important bits
Javascript: the important bitsJavascript: the important bits
Javascript: the important bits
 
Refactoring
RefactoringRefactoring
Refactoring
 
Specs2
Specs2Specs2
Specs2
 
Rails2 Pr
Rails2 PrRails2 Pr
Rails2 Pr
 
Tres Gemas De Ruby
Tres Gemas De RubyTres Gemas De Ruby
Tres Gemas De Ruby
 
Introduction to Kotlin
Introduction to KotlinIntroduction to Kotlin
Introduction to Kotlin
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on Android
 
Intro to Scala.js - Scala UG Cologne
Intro to Scala.js - Scala UG CologneIntro to Scala.js - Scala UG Cologne
Intro to Scala.js - Scala UG Cologne
 
EPiServer report generation
EPiServer report generationEPiServer report generation
EPiServer report generation
 
Spock
SpockSpock
Spock
 
Rails 2010 Workshop
Rails 2010 WorkshopRails 2010 Workshop
Rails 2010 Workshop
 
RSpec
RSpecRSpec
RSpec
 
Mongoskin - Guilin
Mongoskin - GuilinMongoskin - Guilin
Mongoskin - Guilin
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to Nodejs
 
Sprout core and performance
Sprout core and performanceSprout core and performance
Sprout core and performance
 
Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript using CoffeeScript, Backbone.js and JasmineRails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
 
Introduction to ReasonML
Introduction to ReasonMLIntroduction to ReasonML
Introduction to ReasonML
 
Testing with Node.js
Testing with Node.jsTesting with Node.js
Testing with Node.js
 
浜松Rails3道場 其の参 Controller編
浜松Rails3道場 其の参 Controller編浜松Rails3道場 其の参 Controller編
浜松Rails3道場 其の参 Controller編
 
Little Big Ruby
Little Big RubyLittle Big Ruby
Little Big Ruby
 

Mehr von Mark

Building Go Web Apps
Building Go Web AppsBuilding Go Web Apps
Building Go Web AppsMark
 
Angular.js Fundamentals
Angular.js FundamentalsAngular.js Fundamentals
Angular.js FundamentalsMark
 
Go(lang) for the Rubyist
Go(lang) for the RubyistGo(lang) for the Rubyist
Go(lang) for the RubyistMark
 
Mangling Ruby with TracePoint
Mangling Ruby with TracePointMangling Ruby with TracePoint
Mangling Ruby with TracePointMark
 
AngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.jsAngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.jsMark
 
A Big Look at MiniTest
A Big Look at MiniTestA Big Look at MiniTest
A Big Look at MiniTestMark
 
A Big Look at MiniTest
A Big Look at MiniTestA Big Look at MiniTest
A Big Look at MiniTestMark
 
GET /better
GET /betterGET /better
GET /betterMark
 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScriptMark
 
Building an API in Rails without Realizing It
Building an API in Rails without Realizing ItBuilding an API in Rails without Realizing It
Building an API in Rails without Realizing ItMark
 
5 Favorite Gems (Lightning Talk(
5 Favorite Gems (Lightning Talk(5 Favorite Gems (Lightning Talk(
5 Favorite Gems (Lightning Talk(Mark
 
CoffeeScript for the Rubyist
CoffeeScript for the RubyistCoffeeScript for the Rubyist
CoffeeScript for the RubyistMark
 
RubyMotion
RubyMotionRubyMotion
RubyMotionMark
 
CoffeeScript for the Rubyist
CoffeeScript for the RubyistCoffeeScript for the Rubyist
CoffeeScript for the RubyistMark
 
DRb and Rinda
DRb and RindaDRb and Rinda
DRb and RindaMark
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairMark
 
Distributed Programming with Ruby/Rubyconf 2010
Distributed Programming with Ruby/Rubyconf 2010Distributed Programming with Ruby/Rubyconf 2010
Distributed Programming with Ruby/Rubyconf 2010Mark
 

Mehr von Mark (17)

Building Go Web Apps
Building Go Web AppsBuilding Go Web Apps
Building Go Web Apps
 
Angular.js Fundamentals
Angular.js FundamentalsAngular.js Fundamentals
Angular.js Fundamentals
 
Go(lang) for the Rubyist
Go(lang) for the RubyistGo(lang) for the Rubyist
Go(lang) for the Rubyist
 
Mangling Ruby with TracePoint
Mangling Ruby with TracePointMangling Ruby with TracePoint
Mangling Ruby with TracePoint
 
AngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.jsAngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.js
 
A Big Look at MiniTest
A Big Look at MiniTestA Big Look at MiniTest
A Big Look at MiniTest
 
A Big Look at MiniTest
A Big Look at MiniTestA Big Look at MiniTest
A Big Look at MiniTest
 
GET /better
GET /betterGET /better
GET /better
 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScript
 
Building an API in Rails without Realizing It
Building an API in Rails without Realizing ItBuilding an API in Rails without Realizing It
Building an API in Rails without Realizing It
 
5 Favorite Gems (Lightning Talk(
5 Favorite Gems (Lightning Talk(5 Favorite Gems (Lightning Talk(
5 Favorite Gems (Lightning Talk(
 
CoffeeScript for the Rubyist
CoffeeScript for the RubyistCoffeeScript for the Rubyist
CoffeeScript for the Rubyist
 
RubyMotion
RubyMotionRubyMotion
RubyMotion
 
CoffeeScript for the Rubyist
CoffeeScript for the RubyistCoffeeScript for the Rubyist
CoffeeScript for the Rubyist
 
DRb and Rinda
DRb and RindaDRb and Rinda
DRb and Rinda
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
 
Distributed Programming with Ruby/Rubyconf 2010
Distributed Programming with Ruby/Rubyconf 2010Distributed Programming with Ruby/Rubyconf 2010
Distributed Programming with Ruby/Rubyconf 2010
 

Kürzlich hochgeladen

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 

Kürzlich hochgeladen (20)

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 

Testing Rich Client Side Apps with Jasmine

  • 1. TESTING RICH *SCRIPT APPLICATIONS WITH RAILS @markbates
  • 2.
  • 3.
  • 4.
  • 5. Finished in 14.41041 seconds 108 examples, 0 failures
  • 6.
  • 7.
  • 8. Video demo of a Todo list application goes here.
  • 9. app/models/todo.rb class Todo < ActiveRecord::Base validates :body, presence: true attr_accessible :body, :completed end
  • 10. spec/models/todo_spec.rb require 'spec_helper' describe Todo do it "requires a body" do todo = Todo.new todo.should_not be_valid todo.errors[:body].should include("can't be blank") todo.body = "Do something" todo.should be_valid end end
  • 11. app/controllers/todos_controller.rb class TodosController < ApplicationController respond_to :html, :json def index respond_to do |format| format.html {} format.json do @todos = Todo.order("created_at asc") respond_with @todos end end end def show @todo = Todo.find(params[:id]) respond_with @todo end def create @todo = Todo.create(params[:todo]) respond_with @todo end def update @todo = Todo.find(params[:id]) @todo.update_attributes(params[:todo]) respond_with @todo end def destroy @todo = Todo.find(params[:id]) @todo.destroy respond_with @todo end end
  • 12. spec/controllers/todos_controller_spec.rb require 'spec_helper' it "responds with errors" do expect { describe TodosController do post :create, todo: {}, format: 'json' let(:todo) { Factory(:todo) } response.should_not be_successful json = decode_json(response.body) describe 'index' do json.errors.should have(1).error json.errors.body.should include("can't be blank") context "HTML" do }.to_not change(Todo, :count) end it "renders the HTML page" do get :index end response.should render_template(:index) end assigns(:todos).should be_nil end describe 'update' do end context "JSON" do context "JSON" do it "updates a todo" do put :update, id: todo.id, todo: {body: "do something else"}, format: 'json' it "returns JSON for the todos" do get :index, format: "json" response.should be_successful todo.reload response.should_not render_template(:index) todo.body.should eql "do something else" assigns(:todos).should_not be_nil end end it "responds with errors" do end put :update, id: todo.id, todo: {body: ""}, format: 'json' end response.should_not be_successful json = decode_json(response.body) describe 'show' do json.errors.should have(1).error json.errors.body.should include("can't be blank") context "JSON" do end it "returns the todo" do end get :show, id: todo.id, format: 'json' end response.should be_successful response.body.should eql todo.to_json describe 'destroy' do end context "JSON" do end it "destroys the todo" do end todo.should_not be_nil expect { describe 'create' do delete :destroy, id: todo.id, format: 'JSON' }.to change(Todo, :count).by(-1) context "JSON" do end it "creates a new todo" do end expect { post :create, todo: {body: "do something"}, format: 'json' end response.should be_successful end }.to change(Todo, :count).by(1) end
  • 13. app/views/todos/index.html.erb <form class='form-horizontal' id='todo_form'></form> <ul id='todos' class="unstyled"></ul> <script> $(function() { new OMG.Views.TodosApp(); }) </script>
  • 15. app/assets/javascripts/views/todo_view.js.coffee class OMG.Views.TodoView extends OMG.Views.BaseView tagName: 'li' template: JST['todos/_todo'] events: 'change [name=completed]': 'completedChecked' 'click .delete': 'deleteClicked' initialize: -> @model.on "change", @render @render() render: => $(@el).html(@template(todo: @model)) if @model.get("completed") is true @$(".todo-body").addClass("completed") @$("[name=completed]").attr("checked", true) return @ completedChecked: (e) => @model.save(completed: $(e.target).attr("checked")?) deleteClicked: (e) => e?.preventDefault() if confirm("Are you sure?") @model.destroy() $(@el).remove()
  • 16. HOW DO WE TEST THIS?
  • 19. https://github.com/pivotal/jasmine It’s like RSpec for JavaScript* * or CoffeeScript
  • 20. JavaScript example: describe('panda',function(){ it('is happy',function(){ expect(panda).toBe('happy'); }); }); CoffeeScript example: describe 'panda', -> it 'is happy', -> expect(panda).toBe('happy')
  • 21.
  • 22. MATCHERS • expect(x).toEqual(y) • expect(x).toBeFalsy() • expect(x).toBe(y) • expect(x).toContain(y) • expect(x).toMatch(pattern) • expect(x).toBeLessThan(y) • expect(x).toBeDefined() • expect(x).toBeGreaterThan(y) • expect(x).toBeUndefined() • expect(function() {fn();}).toThrow(e) • expect(x).toBeNull() • expect(x).toBeTruthy()
  • 23. NOT-MATCHERS • expect(x).not.toEqual(y) • expect(x).not.toBeFalsy() • expect(x).not.toBe(y) • expect(x).not.toContain(y) • expect(x).not.toMatch(pattern) • expect(x).not.toBeLessThan(y) • expect(x).not.toBeDefined() • expect(x).not.toBeGreaterThan(y) • expect(x).not.toBeUndefined() • expect(function() {fn();}).not.toThrow(e) • expect(x).not.toBeNull() • expect(x).not.toBeTruthy()
  • 24. JASMINE WITH RAILS • gem 'evergreen', '>= 1.0.0', :require => 'evergreen/rails' • gem 'capybara-webkit'
  • 25.
  • 26.
  • 29. spec/javascripts/spec_helper.coffee # Require the appropriate asset-pipeline files: require '/assets/application.js' # Any other testing specific code here... # Custom matchers, etc.... beforeEach -> @_page = $('#test')
  • 30. app/assets/javascript/greeter.js.coffee class @Greeter constructor: (@name) -> greet: -> "Hi #{@name}"
  • 31. spec/javascripts/greeter_spec.coffee describe "Greeter", -> beforeEach -> @greeter = new Greeter("Mark") it "greets someone", -> expect(@greeter.greet()).toEqual("Hi Mark")
  • 32.
  • 34. config/environments/development.rb OMG::Application.configure do # ... config.assets.paths << Rails.root.join("spec", "javascripts", "support") end
  • 35. MATCHERS • toBe(jQuerySelector) • toExist() • toBeChecked() • toHaveAttr(attributeName, attributeValue) • toBeEmpty() • toHaveBeenTriggeredOn(selector) • toBeHidden() • toHaveClass(className) • toBeSelected() • toHaveData(key, value) • toBeVisible() • toHaveHtml(string) • toContain(jQuerySelector)
  • 36. spec/javascripts/spec_helper.coffee # Require the appropriate asset-pipeline files: require '/assets/application.js' require '/assets/jasmine.jquery.js' # Any other testing specific code here... # Custom matchers, etc.... beforeEach -> @_page = $('#test')
  • 37. app/assets/javascripts/views/todo_view.js.coffee class OMG.Views.TodoView extends OMG.Views.BaseView tagName: 'li' template: JST['todos/_todo'] events: 'change [name=completed]': 'completedChecked' 'click .delete': 'deleteClicked' initialize: -> @model.on "change", @render @render() render: => $(@el).html(@template(todo: @model)) if @model.get("completed") is true @$(".todo-body").addClass("completed") @$("[name=completed]").attr("checked", true) return @ completedChecked: (e) => @model.save(completed: $(e.target).attr("checked")?) deleteClicked: (e) => e?.preventDefault() if confirm("Are you sure?") @model.destroy() $(@el).remove()
  • 38. spec/javascripts/views/todos/todo_view_spec.coffee describe "OMG.Views.TodoView", -> beforeEach -> @collection = new OMG.Collections.Todos() @model = new OMG.Models.Todo(id: 1, body: "Do something!", completed: false) @view = new OMG.Views.TodoView(model: @model, collection: @collection) @_page.html(@view.el)
  • 39. spec/javascripts/views/todos/todo_view_spec.coffee describe "model bindings", -> it "re-renders on change", -> expect($('.todo-body')).toHaveText("Do something!") @model.set(body: "Do something else!") expect($('.todo-body')).toHaveText("Do something else!")
  • 40. spec/javascripts/views/todos/todo_view_spec.coffee describe "displaying of todos", -> it "contains the body of the todo", -> expect($('.todo-body')).toHaveText("Do something!") it "is not marked as completed", -> expect($('[name=completed]')).not.toBeChecked() expect($('.todo-body')).not.toHaveClass("completed") describe "completed todos", -> beforeEach -> @model.set(completed: true) it "is marked as completed", -> expect($('[name=completed]')).toBeChecked() expect($('.todo-body')).toHaveClass("completed")
  • 41. spec/javascripts/views/todos/todo_view_spec.coffee describe "checking the completed checkbox", -> beforeEach -> expect($('[name=completed]')).not.toBeChecked() $('[name=completed]').click() it "marks it as completed", -> expect($('[name=completed]')).toBeChecked() expect($('.todo-body')).toHaveClass("completed") describe "unchecking the completed checkbox", -> beforeEach -> @model.set(completed: true) expect($('[name=completed]')).toBeChecked() $('[name=completed]').click() it "marks it as not completed", -> expect($('[name=completed]')).not.toBeChecked() expect($('.todo-body')).not.toHaveClass("completed")
  • 42. app/assets/javascripts/todos/todo_view.coffee class OMG.Views.TodoView extends OMG.Views.BaseView # ... deleteClicked: (e) => e?.preventDefault() if confirm("Are you sure?") @model.destroy() $(@el).remove()
  • 43.
  • 44. MATCHERS • expect(x).toHaveBeenCalled() • expect(x).toHaveBeenCalledWith(arguments) • expect(x).not.toHaveBeenCalled() • expect(x).not.toHaveBeenCalledWith(arguments)
  • 45. TRAINING SPIES • spyOn(x, 'method').andCallThrough() • spyOn(x, 'method').andReturn(arguments) • spyOn(x, 'method').andThrow(exception) • spyOn(x, 'method').andCallFake(function)
  • 46. spec/javascripts/views/todos/todo_view_spec.coffee describe "clicking the delete button", -> describe "if confirmed", -> beforeEach -> spyOn(window, "confirm").andReturn(true) it "will remove the todo from the page", -> expect(@_page.html()).toContain($(@view.el).html()) $(".delete").click() expect(@_page.html()).not.toContain($(@view.el).html()) describe "if not confirmed", -> beforeEach -> spyOn(window, "confirm").andReturn(false) it "will not remove the todo from the page", -> expect(@_page.html()).toContain($(@view.el).html()) $(".delete").click() expect(@_page.html()).toContain($(@view.el).html())
  • 48. spec/javascripts/spec_helper.coffee # Require the appropriate asset-pipeline files: require '/assets/application.js' require '/assets/jasmine.jquery.js' # Any other testing specific code here... # Custom matchers, etc.... beforeEach -> @_page = $('#test') @addMatchers toContainView: (view) -> actual_html = $(@actual).html() view_html = $(view.el).html() @message = -> "Expected #{actual_html} to contain #{view_html}" return @env.contains_(actual_html, view_html)
  • 49. spec/javascripts/views/todos/todo_view_spec.coffee describe "clicking the delete button", -> describe "if confirmed", -> beforeEach -> spyOn(window, "confirm").andReturn(true) it "will remove the todo from the page", -> expect(@_page).toContainView(@view) $(".delete").click() expect(@_page).not.toContainView(@view) describe "if not confirmed", -> beforeEach -> spyOn(window, "confirm").andReturn(false) it "will not remove the todo from the page", -> expect(@_page).toContainView(@view) $(".delete").click() expect(@_page).toContainView(@view)
  • 50. WHAT ABOUT AJAX REQUESTS?
  • 51. app/assets/javascripts/views/todos/todo_list_view.js.coffee class OMG.Views.TodosListView extends OMG.Views.BaseView el: "#todos" initialize: -> @collection.on "reset", @render @collection.on "add", @renderTodo @collection.fetch() render: => $(@el).html("") @collection.forEach (todo) => @renderTodo(todo) renderTodo: (todo) => view = new OMG.Views.TodoView(model: todo, collection: @collection) $(@el).prepend(view.el)
  • 52. spec/javascripts/views/todos/todo_list_view_spec.coffee describe "OMG.Views.TodosListView", -> beforeEach -> @_page.html("<ul id='todos'></ul>") @collection = new OMG.Collections.Todos() @view = new OMG.Views.TodosListView(collection: @collection) it "fetches the collection", -> expect(@collection.length).toEqual(2) it "renders the todos from the collection", -> expect($(@view.el).html()).toMatch("Do something!") expect($(@view.el).html()).toMatch("Do something else!") it "renders new todos added to the collection", -> @collection.add(new OMG.Models.Todo(body: "Do another thing!")) expect($(@view.el).html()).toMatch("Do another thing!")
  • 53. rake spec:javascripts ...FF........... Failed: OMG.Views.TodosListView fetches the collection. Expected 0 to equal 2. in : Failed: OMG.Views.TodosListView renders the todos from the collection. Expected '' to match 'Do something!'. in : Finished in 3.12 seconds 16 examples, 2 failures
  • 54.
  • 56. spec/javascripts/spec_helper.coffee # Require the appropriate asset-pipeline files: require '/assets/application.js' require '/assets/jasmine.jquery.js' require '/assets/mock-ajax.js' require '/assets/mock_responses.js' # Any other testing specific code here... # Custom matchers, etc.... beforeEach -> @_page = $('#test') @addMatchers toContainView: (view) -> actual_html = $(@actual).html() view_html = $(view.el).html() @message = -> "Expected #{actual_html} to contain #{view_html}" return @env.contains_(actual_html, view_html)
  • 57. 1. DEFINE TEST RESPONSE(S)
  • 58. spec/javascripts/support/mock_responses.coffee @Mocks = todos: todo_1: status: 200 responseText: '{"body":"Do something!","completed":false,"id":1}' collection: status: 200 responseText: ''' [ {"body":"Do something!","completed":false,"id":1}, {"body":"Do something else!","completed":false,"id":2} ]'''
  • 59. 2. INSTALL THE MOCK(S)
  • 60. spec/javascripts/views/todos/todo_list_view_spec.coffee describe "OMG.Views.TodosListView", -> beforeEach -> jasmine.Ajax.useMock() @_page.html("<ul id='todos'></ul>") @collection = new OMG.Collections.Todos() @view = new OMG.Views.TodosListView(collection: @collection) it "fetches the collection", -> expect(@collection.length).toEqual(2) it "renders the todos from the collection", -> expect($(@view.el).html()).toMatch("Do something!") expect($(@view.el).html()).toMatch("Do something else!") it "renders new todos added to the collection", -> @collection.add(new OMG.Models.Todo(body: "Do another thing!")) expect($(@view.el).html()).toMatch("Do another thing!")
  • 62. spec/javascripts/views/todos/todo_list_view_spec.coffee describe "OMG.Views.TodosListView", -> beforeEach -> jasmine.Ajax.useMock() @_page.html("<ul id='todos'></ul>") @collection = new OMG.Collections.Todos() @view = new OMG.Views.TodosListView(collection: @collection) request = mostRecentAjaxRequest() request.response(Mocks.todos.collection) it "fetches the collection", -> expect(@collection.length).toEqual(2) it "renders the todos from the collection", -> expect($(@view.el).html()).toMatch("Do something!") expect($(@view.el).html()).toMatch("Do something else!") it "renders new todos added to the collection", -> @collection.add(new OMG.Models.Todo(body: "Do another thing!")) expect($(@view.el).html()).toMatch("Do another thing!")
  • 63. rake spec:javascripts ................ Finished in 2.94 seconds 16 examples, 0 failures
  • 64. THANKS @markbates • jasmine https://github.com/pivotal/jasmine • jasmine-jquery https://github.com/velesin/jasmine-jquery/ • jasmine-ajax https://github.com/pivotal/jasmine-ajax • evergreen https://github.com/jnicklas/evergreen • capybara-webkit https://github.com/thoughtbot/capybara-webkit • Programming in CoffeeScript http://books.markbates.com

Hinweis der Redaktion

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n
  77. \n
  78. \n
  79. \n
  80. \n
  81. \n
  82. \n
  83. \n
  84. \n
  85. \n
  86. \n
  87. \n
  88. \n
  89. \n
  90. \n
  91. \n
  92. \n
  93. \n
  94. \n
  95. \n
  96. \n
  97. \n
  98. \n
  99. \n
  100. \n
  101. \n
  102. \n
  103. \n
  104. \n
  105. \n
  106. \n
  107. \n
  108. \n
  109. \n
  110. \n
  111. \n
  112. \n
  113. \n
  114. \n
  115. \n
  116. \n
  117. \n
  118. \n
  119. \n
  120. \n
  121. \n
  122. \n
  123. \n
  124. \n
  125. \n
  126. \n
  127. \n
  128. \n
  129. \n
  130. \n
  131. \n
  132. \n
  133. \n
  134. \n
  135. \n
  136. \n
  137. \n
  138. \n
  139. \n
  140. \n
  141. \n
  142. \n
  143. \n
  144. \n
  145. \n
  146. \n
  147. \n
  148. \n
  149. \n
  150. \n
  151. \n
  152. \n
  153. \n
  154. \n
  155. \n
  156. \n
  157. \n
  158. \n
  159. \n
  160. \n
  161. \n
  162. \n
  163. \n
  164. \n
  165. \n
  166. \n
  167. \n
  168. \n
  169. \n