Testing Javascript with Jasmine
Upcoming SlideShare
Loading in...5
×
 

Testing Javascript with Jasmine

on

  • 7,693 views

Slides from my Lonestar Ruby Conf 2011 presentation. ...

Slides from my Lonestar Ruby Conf 2011 presentation.

*** Video of presentation: http://confreaks.com/videos/2531-lsrc2011-testing-javascript-with-jasmine ***

Agenda:
- Briefly cover why you should unit test
- Discuss what Jasmine is and isn't
- Show syntax with comparisons to RSpec
- Jasmine with:
- Vanilla JavaScript
- Jasmine with jQuery
- Jasmine with Ruby (not Rails)
- Jasmine with Rails
- Evergreen
- capybara-webkit
- Where does CoffeeScript, node.js, etc. fit in?
- Other helpful libraries/Wrap-up

Statistics

Views

Total Views
7,693
Views on SlideShare
7,287
Embed Views
406

Actions

Likes
8
Downloads
67
Comments
0

5 Embeds 406

http://willcode4beer.net 370
http://kaneshin.hateblo.jp 27
http://feeds.feedburner.com 6
http://posterous.com 2
https://twimg0-a.akamaihd.net 1

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • Tim Tyrrell, from Chicago, moved to Austin a year and a half ago. I work as a Rails Developer at PeopleAdmin in Austin, TX.  We have a SaaS talent management application with about 700 customers in the higher education, government, and non-profit sectors. Post jobs, apply to jobs, hiring workflows, etc.
  • I appreciate you attending my session. I know that you have a choice today on which session to attend, so I plan to make a good use of your time. In accordance, I want to set your expectations by displaying my agenda. It is pretty slide heavy talk with lots of different code examples, but we will blow through them, so don't let that scare you. I know that I am what is standing between you and chicken fried chicken, so I will tread lightly here. This is an intro to testing with jasmine. Cover a lot of stuff briefly. So if you have ever worked with Jasmine, you may want to bail right now.
  • - A lot of us use Ruby through Rails or Sinatra, so we have views, and these views use javascript. - I know that it is a gap in most applications. I think that is is something that might be scary but I want to show you that we can fill this gap and it is not very difficult. Although, it will take a mindset change.
  • - it is code! - it can be very complex! -There was a time where it was though of as a toy language, but that misconception has obviously been blown away. -What is the problem? I think it's viewed like the wild wild west. You write a bunch of procedural script inside the views and any thought of testing code like that is impossible. Procedural  non-modular code, is horribly difficult to test. so right off the bat, you need to treat your javascript code with respect and care that you may backend code. Other perceived problems? Testing javascript is hard to get setup. - Why now? With javascript being more of an important piece of our software, with all this focus shifting to javascript, we need to do it right. Unit testing is the right way to do it. 
  • - I know people say testing slows you down, but the reality is, a deployment that fails is going to slow you down. Refactoring some code and breaking half your application is going to slow you down. - yes, there are such things as spikes and prototypes where testing doesn't make sense. - what Obie said - testing is not easy. A know a number of folks in this room that have been doing it for 5+ years, and they are still learning new things and changing their opinions on the best way to do things. 
  • Don't be this guy. You don't have to test-drive it, but you need to test it. The like unit testing because I don't want to get paged in the middle of the night.
  • - Created by Pivitol Labs - there are other JS testing frameworks: QUnit is the most obvious one, which is used by the jQuery project. QUnit is more of a standard assertion framework where as Jasmine has the BDD style-syntax
  • - super easy to get going and is super easy to use with Rails - Familiar RSpec Syntax (context switching is a killer) - It has a RubyGem for Ruby, Rails, or even Node.js. Also, you can use it without Ruby. - Easy to plug into a CI server, headless or not - Does not require the DOM - It does not depend on any other javascript frameworks - big community around it. Lot's of plugins and it is being pushed
  • - This is for testing javascript in isolation. "Unit testing"   - More like Test::Unit or RSpec, JUnit, NUnit, etc.   - Not Cucumber, Capybara, etc.
  • Differences? - With Rspec I could remove the double-quotes from "Calculate" and it will new that object up for us as the subject. - Rspec uses blocks and Jasmine uses anonymous functions - RSpec allows you to switch out the word describe for context and also there is some other syntactical magic you can us, Jasmine is straight forward.
  • Differences? - this is the setup/teardown events - you can declare @calc in the before block in RSpec and it will be available in other blocks. Not so with Jasmine.
  • - RSpec has different methods that define expectations, but Jasmine only has "expect" which is passed an argument and then chained with matchers. - To test negation you just chain on a "not" - Noel and the one assertion per "it"
  • spec_helper.rb
  • #spec helper.js
  • - A "onSpy" call will remove the specified function from the object and replace it with a spy. This spy will absorb all calls to that method and you can optionally chain a return method on to it.
  • - Test-Driven Development Write a failing test, make it pass, then make it better. Write a test before you write the code to make it pass. I will cover this quickly, it is something we have all heard a million times(I hope!). 
  • - Attention span- Keeps me focused on one thing at a time. If I have a failing test, I know exactly what I am working on and will not bounce off onto a tangent or down a rabbit hole.   - Helps you design your API, because you are utilizing it for your tests - Helps me sleep at night. I can deploy and not be scared to death that something will break. - Keeps me moving fast. Make a change, run the tests, move on. Don't need to open the browser and click. - Give me confidence in major refactorings. (Same as above)
  • - TDD manner!!!
  • - open the html in a browser
  • -  gem 'jasmine' if bundler
  • - create a folder
  • - check out the default specs and functions - explain conventions. *Spec.js
  • - fire up the web server
  • - obviously used with Continuous Integration server. Selenium is run, browser opens closes, etc. - run it in the background while you work
  • Jasmine has a number of libraries, one of them deals with jQuery a set of custom matchers for jQuery framework an API for handling HTML fixtures in your specs
  • dom matchers, not object matchers
  • - find grained control of your text - if you are going to refactor your code or add more features, you don't want to break anything. You need to pin down you existing functionality to keep yourself moving fast.
  • Added a couple libraries and also a fixture directory.
  • - no ruby required here
  • - set the HTML or load a fixture from spec/javascripts - clears it after each run
  • - using a jQuery selecting in the expect matcher
  • - pinning down functionality
  • Also you can rake jasmine:ci Dr. Phil next.
  • Alright, that was the normal Jasmine way to get things set it. It is easy. But, it can be a little easier. There is a better way.
  • Best of breed. Templates, no generators, and coffeescript support. Defaults with Selenium, but you can rock it with Capybara, whatever - not a stand alone, you need ruby (obviously)
  • - folder conventions
  • - CoffeeScript next - recap
  • install node.js install npm install coffeescript
  • - no different than the other evergreen usage of calculator_spec.js
  • side by side.
  • - this is how I felt after I did that. so what did I do? 1. installed the evergreen gem 2. mimicked a folder structure 3. added a file of a coffee extension
  • capybara-webkit depends on a WebKit implementation from Qt, a cross-platform development toolkit. jasmine-headless-webkit uses the QtWebKit widget to run your specs without needing to render a pixel. It's nearly as fast as running in a JavaScript engine like Node.js, and, since it's a real browser environment, all the modules you would normally use, like jQuery and Backbone, work without any modifications.
  • - guard-jasmine-headless-webkit
  • - big problems - you get to work with me
  • THANKS FOR LISTENING! It is easy to setup and use, and it will help you sleep better at night. Grow a neck beard.

Testing Javascript with Jasmine Testing Javascript with Jasmine Presentation Transcript

  •  
  • Testing JavaScript with Jasmine By: Tim Tyrrell
  • Tim Tyrrell @timtyrrell    
  • Agenda
    • - Briefly cover why you should unit test
    • - Discuss what Jasmine is and isn't
    • - Show syntax with comparisons to RSpec
    • - Jasmine with:
    •     - Vanilla JavaScript
    •     - Jasmine with jQuery
    •     - Jasmine with Ruby (not Rails)
    •     - Jasmine with Rails
    •     - Evergreen
    •     - capybara-webkit
    • - Where does CoffeeScript, node.js, etc. fit in?
    • - Other helpful libraries/Wrap-up
  • Why are you talking about JavaScript at a  Ruby conference?
  • Why unit test JavaScript?
  • "External tests don't help us with internal design" @glv
  • "There is only one way to go truly fast. Do the best job you can. Anything else is slower." @unclebobmartin http://twitter.com/#!/unclebobmartin/status/39438225869254656
  •  
  • What is Jasmine?
  • Why Jasmine?
  • Jasmine is not an integration testing framework.
  • RSpec vs. Jasmine: Structure #RSpec describe "Calculate" do      describe "#add" do          it "should return the sum" do              ...          end      end end //Jasmine describe "Calculate", function(){      describe "#Add", function(){          it "should return the sum", function(){          ...          };      }); });
  • RSpec vs. Jasmine: Before/After #RSpec before(:each) do           @calc = Calculator.new  end    after(:each) do           @calc.reset  end //Jasmine var calc;  beforeEach(function(){     calc = new Calculator(); });    afterEach(function(){     calc.reset();  });
  • RSpec vs. Jasmine: Expectations # RSpec  it "should return the sum" do       calc = Calculator.new        calc.add(1,1).should == 2     calc.add(1,2).should_not == 2 #Use one expectation per 'it'! end   // Jasmine  it("should return the sum", function() {           var calc = new Calculator();       expect(calc.Add(1,1)).toEqual(2);     expect(calc.Add(1,2)).not.toEqual(2); //Use one expectation per 'it'!  });
  • Jasmine Matchers expect(x).toEqual(y)   expect(x).toBe(y);   expect(x).toMatch(pattern) expect(x).toBeDefined() expect(x).toBeNull();   expect(x).toBeTruthy(); expect(x).toBeFalsy(); expect(x).toContain(y); expect(x).toBeLessThan(y); expect(x).toBeGreaterThan(y); expect(fn).toThrow(e);
  • Custom Matchers
    • #RSpec
    • Spec::Matchers.define :be_greater_than_zero do
    •   match do |actual|
    •     actual > 0
    •   end
    • end
    • Spec::Matchers.define :be_zero_when_subtracting do
    •   match do |actual, number|
    •     (actual - number) == 0
    •   end
    • end
  • Custom Matchers
    • // Jasmine
    • beforeEach(function() {
    •   this.addMatchers({
    •     toBeGreaterThanZero: function() {
    •       return this.actual > 0;
    •     }
    •   }),
    •   this.addMatchers({
    •     toBeZeroWhenSubtracting: function(number) {
    •       return (this.actual - number) === 0
    •     }
    •   })
    • });
  • Custom Matchers
    • describe('Custom Matchers', function () {
    •   it('should be greater than 0', function () {
    •     expect(1).toBeGreaterThanZero();
    •   });
    •   it('should equal zero when subtracting two numbers', function() {
    •     expect(5).toBeZeroWhenSubtracting(5);
    •   });
    • });
  • RSpec vs. Jasmine: Stubbing # RSpec  it "should add the numbers" do       calc = Calculator.new       calc.stub!(:add).and_return(3)       calc.add(1,1).should == 3 end  // Jasmine  it("should add the numbers", function() {       var calc = new Calculator();       spyOn(calc, 'add').andReturn(3);      expect(calc.Add(1,1)).toEqual(3);  });
  • TDD? " Red , Green , Refactor"
  • Why do *I* like TDD?
  • Jasmine with "vanilla" JavaScript
    • - Download "Stand alone" 
    •  
    • http://pivotal.github.com/jasmine/download.html
  •  
  •  
  •  
  •  
  • describe("Calculator", function() {    }); Spec for Calculator:
  • describe("Calculator", function() {   var calc;   beforeEach(function(){     calc = new Calculator();   }); }); Spec for Calculator:
  • describe("Calculator", function() {   var calc;   beforeEach(function(){     calc = new Calculator();   }); }); Spec for Calculator:
  • describe("Calculator", function() {   var calc;   beforeEach(function(){     calc = new Calculator();   });   it("should return the sum", function() {     expect(calc.Add(1,1)).toEqual(2);   }); }); Spec for Calculator:
  • Failing Test
  • Create the "Calculator" function
    • function Calculator () {
    • }
  • One passing, one left to fix
  • Add the method
    • function Calculator () {   this.Add = function(num1, num2){     return num1 + num2;   }; }
  • Passed!
  • Jasmine with Ruby(not Rails)
  • $ gem install jasmine
  • $ jasmine init
  • Generated folder structure
  • $ rake jasmine
  • http://localhost:8888
  • $ rake jasmine:ci
  • Jasmine with JQuery
  •      * toBe(jQuerySelector)     * toBeChecked()     * toBeEmpty()     * toBeHidden()     * toBeSelected()     * toBeVisible()     * toContain(jQuerySelector)     * toExist()     * toHaveAttr(attributeName, attributeValue)     * toHaveBeenTriggeredOn(selector)     * toHaveClass(className)     * toHaveData(key, value)     * toHaveHtml(string)     * toHaveId(id)     * toHaveText(string)     * toHaveValue(value)     * toBeDisabled() jasmine-jquery Matchers
  • &quot;A lightweight, easy to use Javascript <span> injector for radical Web Typography&quot;
  • @davatron5000
  • Lettering.js <h1 class=&quot;fancy_title&quot;>Some Title</h1> <script>   $(document).ready(function() {     $(&quot;.fancy_title&quot;).lettering();   }); </script>
  • Lettering.js results
    • <h1 class=&quot;fancy_title&quot;>
    •   <span class=&quot;char1&quot;>S</span>
    •   <span class=&quot;char2&quot;>o</span>
    •   <span class=&quot;char3&quot;>m</span>
    •   <span class=&quot;char4&quot;>e</span>
    •   <span class=&quot;char5&quot;></span>
    •   <span class=&quot;char6&quot;>T</span>
    •   <span class=&quot;char7&quot;>i</span>
    •   <span class=&quot;char8&quot;>t</span>
    •   <span class=&quot;char9&quot;>l</span>
    •   <span class=&quot;char10&quot;>e</span>
    • </h1>
  • Standalone folder structure + more
    • <!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;
    •   &quot;http://www.w3.org/TR/html4/loose.dtd&quot;>
    • <html>
    • <head>
    •   <title>Jasmine Test Runner</title>
    •   <link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;lib/jasmine-1.0.2/jasmine.css&quot;>
    •   <script type=&quot;text/javascript&quot; src=&quot;lib/jasmine-1.0.2/jasmine.js&quot;></script>
    •   <script type=&quot;text/javascript&quot; src=&quot;lib/jasmine-1.0.2/jasmine-html.js&quot;></script>
    •   <script type=&quot;text/javascript&quot; src=&quot;lib/jquery-1.6.1.min.js&quot;></script>
    •   <script type=&quot;text/javascript&quot; src=&quot;lib/jasmine-jquery-1.2.0.js&quot;></script>
    •   <script type=&quot;text/javascript&quot; src=&quot;lib/jquery.lettering-0.6.1.min.js&quot;></script>
    •   <script type=&quot;text/javascript&quot; src=&quot;spec/LetteringSpec.js&quot;></script>
    • </head>
    • <body>
    • <script type=&quot;text/javascript&quot;>
    •   jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
    •   jasmine.getEnv().execute();
    • </script>
    • </body>
    • </html>
    • describe(&quot;Lettering&quot;, function(){
    •   beforeEach(function(){
    •     //loadFixtures('myfixture.html');
    •     setFixtures('<h1 class=&quot;fancy_title&quot;>Some Title</h1>');
    •     $('.fancy_title').lettering();
    •   });
    • });
    • describe(&quot;Lettering&quot;, function(){
    •   beforeEach(function(){
    •     //loadFixtures('myfixture.html');
    •     setFixtures('<h1 class=&quot;fancy_title&quot;>Some Title</h1>');
    •     $('.fancy_title').lettering();
    •   });
    •   it(&quot;should be visible&quot;, function() {
    •     expect($('.fancy_title')).toBeVisible();
    •   });
    • });
    • describe(&quot;Lettering&quot;, function(){
    •   beforeEach(function(){
    •     //loadFixtures('myfixture.html');
    •     setFixtures('<h1 class=&quot;fancy_title&quot;>Some Title</h1>');
    •     $('.fancy_title').lettering();
    •   });
    •   it(&quot;should be visible&quot;, function() {
    •     expect($('.fancy_title')).toBeVisible();
    •   });
    •   it(&quot;should have 10 spans&quot;, function(){
    •     expect($('.fancy_title > span').length).toEqual(10);
    •   });
    • });
    • describe(&quot;Lettering&quot;, function(){
    •   beforeEach(function(){
    •     //loadFixtures('myfixture.html');
    •     setFixtures('<h1 class=&quot;fancy_title&quot;>Some Title</h1>');
    •     $('.fancy_title').lettering();
    •   });
    •   it(&quot;should be visible&quot;, function() {
    •     expect($('.fancy_title')).toBeVisible();
    •   });
    •   it(&quot;should have 10 spans&quot;, function(){
    •     expect($('.fancy_title > span').length).toEqual(10);
    •   });
    •   it(&quot;should contain 'S' as the text of the first span&quot;, function(){
    •     expect($('.fancy_title > span:first').text()).toEqual('S');
    •   });
    • });
    • describe(&quot;Lettering&quot;, function(){
    •   beforeEach(function(){
    •     //loadFixtures('myfixture.html');
    •     setFixtures('<h1 class=&quot;fancy_title&quot;>Some Title</h1>');
    •     $('.fancy_title').lettering();
    •   });
    •   it(&quot;should be visible&quot;, function() {
    •     expect($('.fancy_title')).toBeVisible();
    •   });
    •   it(&quot;should have 10 spans&quot;, function(){
    •     expect($('.fancy_title > span').length).toEqual(10);
    •   });
    •   it(&quot;should contain 'S' as the text of the first span&quot;, function(){
    •     expect($('.fancy_title > span:first').text()).toEqual('S');
    •   });
    •   it(&quot;should have a class of 'char1' on the first span&quot;, function(){
    •     expect($('.fancy_title > span:first')).toHaveClass('char1');
    •   });
    • });
  • Jasmine with Rails gem &quot;jasmine&quot;  $ bundle install $ jasmine init (optional) $ rake jasmine or  $ rake jasmine:ci http://localhost:8888
  • Generated Rails spec folder
  • Generated default javascript objects
  • $ rake jasmine 
  •  
  • $ gem install evergreen
  • gem install evergreen
  • Evergreen default folder paths  (not generated)
  • $ evergreen serve
  • $ evergreen run
  • Evergreen and Rails 3
    • gem 'evergreen', :require => evergreen/rails'localhost:3000/evergreen
    • rake spec:javascripts
  • What about CoffeeScript???
  • Installing CoffeeScript
    • brew install node.js (then add to PATH, NODE_PATH)
    • curl http://npmjs.org/install.sh | sh
    • npm install -g coffee-script
  • Add  a &quot;_spec.coffee&quot; file
    • require('/calculator.js')
    • describe 'Calculator', ->
    •   beforeEach ->
    •     @calc = new Calculator
    •   it 'should return the sum', ->
    •     expect(@calc.Add(1,1)).toEqual(2)
    • require('/calculator.js')
    • describe 'Calculator', ->
    •   beforeEach ->
    •     @calc = new Calculator
    •   it 'should return the sum', ->
    •     expect(@calc.Add(1,1)).toEqual(2)
    • require('/calculator.js')
    • describe 'Calculator', ->
    •   beforeEach ->
    •     @calc = new Calculator
    •   it 'should return the sum', ->
    •     expect(@calc.Add(1,1)).toEqual(2)
  • #winning
  •  
  • Headless Browser Install
    • - Download and install Qt 4.7+ binary package
    • - gem install capybara-webkit -v=1.0.0.beta4
    • - create &quot;config/evergreen.rb&quot;
  • Configure Evergreen
    • require 'capybara-webkit'
    • Evergreen.configure do |config|
    •   #config.driver = :selenium
    •   config.driver = :webkit
    •   config.public_dir = 'public'
    •   config.spec_dir = 'spec/javascripts'
    • end
  • Using a Headless Browser
    • #selenium
    • Finished in 3.92 seconds
    • 2 examples, 0 failures
    • #webkit
    • Finished in 1.05 seconds
    • 2 examples, 0 failures
  • Node.js + Jasmine
    • &quot;jasmine-node&quot;
  • Any other cool utilities?
  • Rosie
    • &quot; Rosie is a factory for building JavaScript objects, mostly useful for setting up test data. It is inspired by  factory_girl . &quot;
    • https://github.com/bkeepers/rosie
  • Summer Breeze
    • &quot; Summer Breeze is a tool for generating fixture DOM output for Jasmine JavaScript tests directly from your Rails Views with a simple DSL for specifying fixtures &quot;
    • https://github.com/noelrappin/summer_breeze
  • jasminerice
    • &quot;Pain free coffeescript testing under Rails 3.1&quot;
    • https://github.com/bradphelan/jasminerice
  • http://try-jasmine.heroku.com
  • Wrap Up
    • 1. Unit testing is important
    • 2. Javascript can and should be unit tested
    • 3. Jasmine is easy to setup and use
  •  
  • Thank You! @timtyrrell Questions? Did I miss any cool plugins? http://bit.ly/jasmine-lsrc http://spkr8.com/t/7818
  • http://pivotal.github.com/jasmine/ https://github.com/jnicklas/evergreen https://github.com/velesin/jasmine-jquery https://github.com/thoughtbot/capybara-webkit http://obtiva.com/blog/112-javascript-specs-with-jasmine-a-primer-for-rubyists-part-1 http://obtiva.com/blog/119 http://daverupert.com/2010/09/lettering-js/ http://www.engineyard.com/university/screencasts/javascript-tests-with-jasmine http://blog.levid.com/?p=29 http://blog.carbonfive.com/2010/10/21/rspec-best-practices/ https://github.com/johnbintz/guard-jasmine-headless-webkit References: