Compare commits

...

No commits in common. "gh-pages" and "master" have entirely different histories.

53 changed files with 1489 additions and 1492 deletions

BIN
.DS_Store vendored

Binary file not shown.

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.DS_Store
.sass-cache/
_export/
_site/

9
Gemfile Normal file
View File

@ -0,0 +1,9 @@
source :rubygems
gem 'attentive', :path => '../attentive'
gem 'rack-livereload'
gem 'guard'
gem 'guard-livereload'
gem 'thin'

172
Gemfile.lock Normal file
View File

@ -0,0 +1,172 @@
PATH
remote: ../attentive
specs:
attentive (0.0.1)
backbone-rails
coffee-script
compass (~> 0.12.rc)
haml
naturalsort (~> 1.1.1)
nokogiri
pygments.rb
rack (~> 1.4.0)
rdiscount
selenium-webdriver
sinatra
sprockets
sprockets-sass
sprockets-vendor_gems
thor
GEM
remote: http://rubygems.org/
specs:
actionmailer (3.2.2)
actionpack (= 3.2.2)
mail (~> 2.4.0)
actionpack (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.1)
rack (~> 1.4.0)
rack-cache (~> 1.1)
rack-test (~> 0.6.1)
sprockets (~> 2.1.2)
activemodel (3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
activerecord (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
activesupport (3.2.2)
i18n (~> 0.6)
multi_json (~> 1.0)
addressable (2.2.6)
arel (3.0.2)
backbone-rails (0.9.1)
rails (>= 3.0.0)
blankslate (2.1.2.4)
builder (3.0.0)
childprocess (0.3.1)
ffi (~> 1.0.6)
chunky_png (1.2.5)
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.2.0)
compass (0.12.1)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.1)
daemons (1.1.8)
em-websocket (0.3.6)
addressable (>= 2.1.1)
eventmachine (>= 0.12.9)
erubis (2.7.0)
eventmachine (0.12.10)
execjs (1.3.0)
multi_json (~> 1.0)
ffi (1.0.11)
fssm (0.2.8.1)
guard (1.0.0)
ffi (>= 0.5.0)
thor (~> 0.14.6)
guard-livereload (0.4.0)
em-websocket (>= 0.2.0)
guard (>= 0.10.0)
multi_json (~> 1.0.3)
haml (3.1.4)
hike (1.2.1)
i18n (0.6.0)
journey (1.0.3)
json (1.6.5)
mail (2.4.4)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.18)
multi_json (1.0.4)
naturalsort (1.1.1)
nokogiri (1.5.2)
polyglot (0.3.3)
pygments.rb (0.2.7)
rubypython (~> 0.5.3)
rack (1.4.1)
rack-cache (1.2)
rack (>= 0.4)
rack-livereload (0.3.4)
rack
rack-protection (1.2.0)
rack
rack-ssl (1.3.2)
rack
rack-test (0.6.1)
rack (>= 1.0)
rails (3.2.2)
actionmailer (= 3.2.2)
actionpack (= 3.2.2)
activerecord (= 3.2.2)
activeresource (= 3.2.2)
activesupport (= 3.2.2)
bundler (~> 1.0)
railties (= 3.2.2)
railties (3.2.2)
actionpack (= 3.2.2)
activesupport (= 3.2.2)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
rake (0.9.2.2)
rdiscount (1.6.8)
rdoc (3.12)
json (~> 1.4)
rubypython (0.5.3)
blankslate (>= 2.1.2.3)
ffi (~> 1.0.7)
rubyzip (0.9.6.1)
sass (3.1.15)
selenium-webdriver (2.20.0)
childprocess (>= 0.2.5)
ffi (~> 1.0)
multi_json (~> 1.0)
rubyzip
sinatra (1.3.2)
rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2)
tilt (~> 1.3, >= 1.3.3)
sprockets (2.1.2)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-sass (0.7.0)
sprockets (~> 2.0)
tilt (~> 1.1)
sprockets-vendor_gems (0.1.1)
thin (1.3.1)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.14.6)
tilt (1.3.3)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.32)
PLATFORMS
ruby
DEPENDENCIES
attentive!
guard
guard-livereload
rack-livereload
thin

4
Guardfile Normal file
View File

@ -0,0 +1,4 @@
guard 'livereload' do
watch(%r{^(assets|presentation)})
end

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 171 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 140 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

View File

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 201 KiB

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 43 B

After

Width:  |  Height:  |  Size: 43 B

View File

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
assets/images/beer-cat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
assets/images/cat-meow.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
assets/images/checklist.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

BIN
assets/images/spaghetti.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
assets/images/spy-cat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
assets/images/synergize.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

BIN
assets/images/synergize.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

BIN
assets/images/wet-cat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,4 @@
#= require attentive
#
Attentive.Presentation.setup('#slides')

View File

@ -0,0 +1,43 @@
@import 'attentive';
$color: #d4d2bf;
body, html {
background-color: $color;
}
h1, h2, h3 {
font-family: Acme, sans-serif;
}
div.highlight {
background-color: adjust-lightness($color, -10);
}
.style-image-80-percent {
img {
height: 80%;
}
}
#slides {
@include transition-duration(0.3s);
}
#intro {
width: 90%;
@include clearfix;
position: relative;
img {
width: 30%;
@include float-left;
}
div {
width: 70%;
@include float-right;
}
}

1492
index.html

File diff suppressed because it is too large Load Diff

9
presentation.rb Normal file
View File

@ -0,0 +1,9 @@
require 'rack-livereload'
Attentive.configure do |c|
c.title = "Tea Time: A Beginner's Guide to Jasmine"
c.middleware << Rack::LiveReload
#c.export_size = '1280x1024'
end

View File

@ -0,0 +1,146 @@
!SLIDE
<div id="intro">
<img src="assets/john-drinking-tea.png" />
<div>
<h1>Tea Time</h1>
<h2>A Beginner's Guide to JavaScript Testing using Jasmine</h2>
<h3>By John Bintz</h3>
</div>
</div>
!SLIDE
# Automated testing is important
!SLIDE
# Why is it important?
!SLIDE image-80-percent
<img src="assets/checklist.png" />
!SLIDE
# Fortunately, we're beyond that nowadays
!SLIDE even-larger
``` ruby
describe MyCoolWebsite do
let(:website) { described_class.new }
describe '#cool_method' do
subject { website.cool_method }
let(:oh_yeah) { [ double_cool ] }
let(:double_cool) { 'double cool' }
before do
website.stubs(:whoa_cool).
returns(oh_yeah)
end
it { should == double_cool }
end
end
```
!SLIDE
# But there's more to web apps than Ruby nowadays...
!SLIDE even-larger
``` html
<img src="normal.gif"
onmouseover="this.src='hover.gif'"
onmouseout="this.src='normal.gif'" />
```
!SLIDE even-larger
``` javascript
function showMyCoolTitle(title, length) {
if (length == null) { length = 0; }
if (length <= title.length) {
document.title =
title.substr(0, length);
length++;
setTimeout(function() {
showMyCoolTitle(title, length);
}, 75);
}
}
```
!SLIDE even-larger
``` javascript
window.onload = function() {
showMyCoolTitle(
"My cool website! Whoaaaaa!"
);
}
```
!SLIDE
# jQuery
!SLIDE
# Backbone.js
!SLIDE
# Sprockets and RequireJS
!SLIDE
# Automated testing is important
!SLIDE even-larger
``` ruby
describe MyCoolWebsite do
let(:website) { described_class.new }
describe '#cool_method' do
subject { website.cool_method }
let(:oh_yeah) { [ double_cool ] }
let(:double_cool) { 'double cool' }
before do
website.stubs(:whoa_cool).
returns(oh_yeah)
end
it { should == double_cool }
end
end
```
!SLIDE even-larger
``` coffeescript
describe 'MyCoolWebsiteView', ->
beforeEach ->
@website = new MyCoolWebsiteView()
describe '#coolMethod', ->
doubleCool = 'double cool'
ohYeah = [ doubleCool ]
beforeEach ->
@website.whoaCool = -> ohYeah
it 'should be double cool', ->
expect(@website.coolMethod()).
toEqual(doubleCool)
```
!SLIDE
# Jasmine
!SLIDE
# BDD unit testing framework for JavaScript
!SLIDE
# Platform independent
!SLIDE
# Easily extended
!SLIDE
# Very easy to learn!

View File

@ -0,0 +1,27 @@
!SLIDE
# Follow along!
!SLIDE
## [johnbintz.github.com/tea-time](http://johnbintz.github.com/tea-time/)
!SLIDE
# No need to install anything right now
!SLIDE
# [tryjasmine.com](http://tryjasmine.com/)
!SLIDE
# Specs on the left
!SLIDE
# Code under test on the right
!SLIDE
# Write code in CoffeeScript
!SLIDE
# Ready?
!SLIDE
# Let's go!

View File

@ -0,0 +1,62 @@
!SLIDE
# `describe`
!SLIDE
# Describes a thing or a behavior of a thing
!SLIDE
# Let's describe...
!SLIDE image-80-percent
<img src="assets/relevant-cat.jpg" />
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
# cat behavior descriptions go here
```
!SLIDE
# Something that cats do...
!SLIDE image-80-percent
<img src="assets/cat-meow.jpg" />
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
describe '#meow', ->
# description of the
# meow behavior goes here
```
!SLIDE larger
# John behavior #1
## Use Ruby-style indicators for instance- and class-level methods
``` coffeescript
describe 'John', ->
describe 'spec definitions', ->
it 'should look like you did it in RSpec', ->
```
!SLIDE
# Describe how we expect a cat to meow
!SLIDE
# `it`
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
describe '#meow', ->
it 'should meow correctly', ->
# expectation of a cat meowing
```
!SLIDE
# We have the description...
!SLIDE
# Now let's add the expectations!

View File

@ -0,0 +1,91 @@
!SLIDE
# `expect`
!SLIDE
# What should we get as an output?
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
describe '#meow', ->
it 'should meow correctly', ->
expect(cat.meow()).toEqual('meow')
```
!SLIDE
# Wait, we need a cat.
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
describe '#meow', ->
it 'should meow correctly', ->
cat = new Cat()
expect(cat.meow()).toEqual('meow')
```
!SLIDE even-larger
``` coffeescript
# code-under-test
class @Cat
meow: ->
```
!SLIDE larger
``` javascript
// safety wrapper to prevent global pollution
(function() {
this.Cat = (function() {
function Cat() {}
Cat.prototype.meow = function() {};
return Cat;
})();
})(this) // this is window in a browser
```
!SLIDE
# Run it!
!SLIDE even-larger
```
1 spec, 1 failure
Expected undefined to equal 'meow'.
```
!SLIDE
# Make it meow!
!SLIDE even-larger
``` coffeescript
class @Cat
meow: -> "meow"
```
!SLIDE even-larger
```
1 spec, 0 failures
```
!SLIDE
# Here's what you should have meow...
!SLIDE even-larger
``` coffeescript
# spec
describe 'Cat', ->
describe '#meow', ->
it 'should meow correctly', ->
expect(cat.meow()).toEqual('meow')
```
!SLIDE even-larger
``` coffeescript
# code-under-test
class @Cat
meow: -> "meow"
```

View File

@ -0,0 +1,124 @@
!SLIDE
# What if the cat meows differently based on certain states?
!SLIDE image-80-percent
<img src="assets/hungry-cat.jpg" />
!SLIDE image-80-percent
<img src="assets/cat-carrier.jpg" />
!SLIDE
# Nested `describe`
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
describe '#meow', ->
describe 'hungry', ->
# Cat#meow expectation for when
# the cat is hungry
describe 'going to the vet', ->
# Cat#meow expectation for when
# the cat knows it's vet time
```
!SLIDE even-larger
``` coffeescript
describe 'hungry', ->
it 'should be a mournful meow', ->
cat = new Cat()
cat.state = -> Cat.HUNGRY
# ...just like cat.stubs(:state)
expect(cat.meow()).toEqual("meeeyaow")
```
!SLIDE even-larger
``` coffeescript
describe 'going to the vet', ->
it 'should be an evil meow', ->
cat = new Cat()
cat.state = -> Cat.VET_PSYCHIC
# ...just like the one above
expect(cat.meow()).toEqual("raowwww")
```
!SLIDE image-80-percent
<img src="assets/wet-cat.jpg" />
!SLIDE even-larger
``` coffeescript
cat = new Cat()
```
!SLIDE even-larger
``` ruby
before do
@cat = Cat.new
end
it 'should be a mournful meow' do
@cat.stubs(:state).returns(Cat::HUNGRY)
@cat.meow.should == "meeyaow"
end
```
!SLIDE even-larger
``` ruby
before -> it -> after
```
!SLIDE even-larger
``` ruby
before do
@instance_variable = "yes"
end
it "is in same context as before block" do
@instance_variable.should == "yes"
end
```
!SLIDE even-larger
``` coffeescript
beforeEach -> it -> afterEach
```
!SLIDE even-larger
``` coffeescript
beforeEach ->
@instanceVariable = "yes"
it "should be in the same context", ->
expect(@instanceVariable).toEqual("yes")
```
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
describe '#meow', ->
beforeEach ->
@cat = new Cat()
```
!SLIDE even-larger
``` coffeescript
describe 'hungry', ->
it 'should be a mournful meow', ->
@cat.state = -> Cat.HUNGRY
expect(@cat.meow()).toEqual("meeeyaow")
```
!SLIDE even-larger
``` coffeescript
describe 'going to the vet', ->
it 'should be an evil meow', ->
@cat.state = -> Cat.VET_PSYCHIC
expect(@cat.meow()).toEqual("raowwww")
```

View File

@ -0,0 +1,130 @@
!SLIDE
# A little semantics game...
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
describe '#meow', ->
describe 'hungry', ->
# cat codes
describe 'going to the vet', ->
# moar cat codes
```
!SLIDE
# This works, but it can be clearer
!SLIDE even-larger
``` ruby
describe Cat do
describe '#meow' do
describe 'hungry' do
# cat codes
end
describe 'going to the vet' do
# moar cat codes
end
end
end
```
!SLIDE
# `context`
!SLIDE
# Description of different states for a test
!SLIDE even-larger
``` ruby
alias :context :describe
```
!SLIDE even-larger
``` ruby
describe Cat do
let(:cat) { described_class.new }
# save describe for
# things or behaviors...
describe '#meow' do
subject { cat.meow }
# use context to describe states
context 'hungry' do
# cat codes
end
context 'going to the vet' do
# moar cat codes
end
end
end
```
!SLIDE
# Jasmine doesn't have `context`
!SLIDE
# However...
!SLIDE even-larger
``` coffeescript
this.context = this.describe
```
!SLIDE even-larger
``` coffeescript
this.context = this.describe
describe 'Cat', ->
describe '#meow', ->
context 'hungry', ->
# cat codes
context 'going to the vet', ->
# moar cat codes
```
!SLIDE larger
``` coffeescript
this.context = this.describe
describe 'Cat', ->
describe '#meow', ->
beforeEach ->
@cat = new Cat()
context 'hungry', ->
it 'should be a mournful meow', ->
@cat.state = -> Cat.HUNGRY
expect(@cat.meow()).toEqual("meeeyaow")
context 'going to the vet', ->
it 'should be an evil meow', ->
@cat.state = -> Cat.VET_PSYCHIC
expect(@cat.meow()).toEqual("raowwww")
```
!SLIDE even-larger
``` coffeescript
class @Cat
@HUNGRY = 'hungry'
@VET_PSYCHIC = 'vet psychic'
meow: ->
switch this.state()
when Cat.HUNGRY
"meeeyaow"
when Cat.VET_PSYCHIC
"raowwww"
```
!SLIDE even-larger
```
2 spec, 0 failures
```

View File

@ -0,0 +1,60 @@
!SLIDE
# Matchers
!SLIDE even-larger
``` ruby
cat.meow.should == "meow"
cat.should be_a_kind_of(Cat)
cat.should_not be_hungry
# => cat.hungry?.should == false
```
!SLIDE even-larger
``` coffeescript
expect(cat.meow()).toEqual("meow")
expect(cat.constructor).toEqual(Cat)
expect(cat.isHungry()).not.toBeTruthy()
```
!SLIDE even-larger
# Lots of built in matchers
``` coffeescript
toEqual(object)
toBeTruthy()
toBeFalsy()
toBeGreaterThan()
toBeLessThan()
toBeUndefined()
toContain()
toMatch()
```
!SLIDE even-larger
``` coffeescript
expect(cat.isHungry()).not.toBeTruthy()
```
!SLIDE
# Create your own matchers!
!SLIDE even-larger
``` coffeescript
MyMatchers =
toBeHungry: ->
return @actual.isHungry() == true
beforeEach ->
this.addMatchers(MyMatchers)
```
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
beforeEach ->
@cat = new Cat()
it 'should not be hungry', ->
expect(@cat).not.toBeHungry()
```

View File

@ -0,0 +1,380 @@
!SLIDE even-larger
``` coffeescript
describe
it
expect
toSomething()
beforeEach
afterEach
```
!SLIDE
# Jasmine == unit testing
!SLIDE image-80-percent
<img src="assets/synergize.jpg" />
!SLIDE
# No, this isn't a talk about integration testing
!SLIDE
# Testing the *right* things in your JavaScript unit tests
!SLIDE image-80-percent
<img src="assets/spaghetti.jpg" />
!SLIDE
# John behavior #2
## Mock, stub, and spy on anything that should be handled in an integration test
``` coffeescript
describe 'John', ->
describe 'spec definitions', ->
it 'should keep unit tests as focused as possible', ->
```
!SLIDE image-80-percent
<img src="assets/beer-cat.jpg" />
!SLIDE even-larger
``` gherkin
Feature: Cat Behaviors
Scenario: Hungry cats meow a certain way
Given I have a cat
And the cat is hungry
When the cat meows
Then the meow should be a "meeyaow"
```
!SLIDE even-larger
``` coffeescript
class @Cat
@FOOD_THRESHOLD = 20
@HUNGRY = 'hungry'
constructor: (@foodLevel = 30) ->
meow: ->
switch this.state()
when Cat.HUNGRY
"meeyaow"
state: ->
if @foodLevel < Cat.FOOD_THRESHOLD
Cat.HUNGRY
```
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
describe '#meow', ->
context 'hungry', ->
it 'should be a mournful meow', ->
cat = new Cat()
cat.foodLevel = 15
expect(cat.meow()).
toEqual("meeeyaow")
```
!SLIDE
# A perfectly cromulent test
!SLIDE even-larger
``` coffeescript
class @Cat
meow: ->
switch this.state()
# ^^^ dependent code executed
when Cat.HUNGRY
"meeyaow"
```
!SLIDE
# Why make your unit tests fragile?
!SLIDE even-larger
``` coffeescript
cat.foodLevel = 15
# do we care about food level?
```
!SLIDE even-larger
``` coffeescript
describe 'Cat', ->
describe '#meow', ->
context 'hungry', ->
it 'should be a mournful meow', ->
cat = new Cat()
cat.state = -> Cat.HUNGRY
# ^^^ we just want a hungry cat
expect(cat.meow()).
toEqual("meeeyaow")
```
!SLIDE
# Instance Stubs in JavaScript
## Just replace the method on the instance
``` coffeescript
class @Cat
state: ->
# cat codes
cat = new Cat()
cat.state = -> "whatever"
```
!SLIDE
# Stubs just return something when called
!SLIDE
# Mocks expect to be called
!SLIDE
# Test fails if all mocks are not called
!SLIDE
# Jasmine blurs the line a little
!SLIDE image-80-percent
<img src="assets/spy-cat.jpg" />
!SLIDE
# Spies work like mocks, but with additional abilities
!SLIDE image-80-percent
<img src="assets/flying-cat.jpg" />
!SLIDE even-larger
``` coffeescript
class @Cat
vocalProcessor: (speech) =>
if this.isAirborne()
this.modifyForAirborne(speech)
else
this.modifyForGround(speech)
```
!SLIDE larger
``` coffeescript
describe 'Cat#vocalProcessor', ->
speech = "speech"
beforeEach ->
@cat = new Cat()
context 'airborne', ->
beforeEach ->
spyOn(@cat, 'modifyForAirborne')
@cat.isAirborne = -> true
it 'should be modified for flight', ->
@cat.vocalProcessor(speech)
expect(@cat.modifyForAirborne).
toHaveBeenCalledWith(speech)
```
!SLIDE even-larger
# `spyOn` replaces a method on an instance with a spy method
``` coffeescript
spyOn(@cat, 'modifyForAirborne')
```
!SLIDE
# Can return a value, run code, run the original code, or just wait to be called
!SLIDE
# Two basic ways to make sure a spy is called
!SLIDE even-larger
## `toHaveBeenCalledWith(args...)`
### Called least once with the given parameters
``` coffeescript
expect(@cat.modifyForAirborne).
toHaveBeenCalledWith(speech)
```
!SLIDE even-larger
## `toHaveBeenCalled()`
### Just called, no parameter check
``` coffeescript
expect(@cat.modifyForAirborne).
toHaveBeenCalled()
```
!SLIDE even-larger
# Instance Mocks/Spies in JavaScript
## Use `spyOn`/`toHaveBeenCalled` matchers
``` coffeescript
class @Cat
state: ->
# cat codes
cat = new Cat()
spyOn(cat, 'state')
expect(cat.state).
toHaveBeenCalled()
```
!SLIDE
# `spyOn` works great with class-level stubs and mocks, too
!SLIDE larger
``` coffeescript
class @Cat
@generateFurColor: (base) ->
# magicks to make a fur color given a base
regrowFur: (damagedHairs) ->
for follicle in damagedHairs
follicle.regrow(
Cat.generateFurColor(this.baseColor)
)
```
!SLIDE larger
``` coffeescript
Cat.generateFurColor = ->
"whoops i nuked this method for every other test"
```
!SLIDE larger
``` coffeescript
describe 'Cat#regrowFur', ->
color = 'color'
beforeEach ->
@cat = new Cat()
@follicle = { regrow: -> null }
@follicles = [ follicle ]
spyOn(Cat, 'generateFurColor').
# ^^^ original replaced when done
andReturn(color)
spyOn(@follicle, 'regrow')
it 'should regrow', ->
@cat.regrowFur(@follicles)
expect(@follicle.regrow).
toHaveBeenCalledWith(color)
```
!SLIDE larger
# Class Stubs in JavaScript
## Use `spyOn` to generate stubs so that the original code is replaced after the test
``` coffeescript
class this.Cat
@injectPsychicPowers: (cat) ->
# cat codes
spyOn(Cat, 'injectPsychicPowers').andReturn(psychicCat)
```
!SLIDE
# John behavior #3
## If you have too many mocks/stubs/contexts, your code is too complex
``` coffeescript
describe 'John', ->
describe 'spec definitions', ->
it 'should obey the Law of Demeter as much as possible', ->
it 'should not smell too funny', ->
```
!SLIDE smaller
``` coffeescript
describe 'Cat#fetch', ->
object = null
context 'a mouse', ->
beforeEach ->
object = new Mouse()
context 'fast mouse', ->
it 'should wear down the mouse', ->
# who
context 'slow mouse', ->
it 'should deliver a present to you', ->
# cares
context 'a ball', ->
beforeEach ->
object = new Ball()
context 'ball is bouncing', ->
it 'should cause the cat to leap', ->
# this
context 'ball is rolling', ->
it 'should cause the cat to slide on the floor', ->
# test
context 'a red dot', ->
laser = null
beforeEach ->
laser = new Laser()
context 'laser out of batteries', ->
it 'should not activate', ->
# is
context 'laser functioning', ->
it 'should activate, driving the cat insane', ->
# huge and unmaintainable and silly
```
!SLIDE
# Sometimes you just need a big blob of unit tests
!SLIDE larger
``` coffeescript
# fast and focused!
describe 'Cat#respondsTo', ->
beforeEach ->
@cat = new Cat()
context 'successes', ->
it 'should respond', ->
for request in [ 'kitty kitty',
'pookums',
'hisshead' ]
expect(@cat.respondsTo(request)).
toBeTruthy()
```
!SLIDE larger
``` gherkin
# slow and synergistic!
Scenario Outline: Successful responsiveness
Given I have a cat
When I call it with "<request>"
Then the cat should respond
Examples:
| request |
| kitty kitty |
| pookums |
| hisshead |
```
!SLIDE image-80-percent
<img src="assets/balancing-cat.jpg" />
!SLIDE
# Find what works best for you and stick with it
!SLIDE
## ...until you get sick of it, of course...

View File

@ -0,0 +1,33 @@
!SLIDE
# Using it in your project
!SLIDE
## [github.com/pivotal/jasmine-gem](http://github.com/pivotal/jasmine-gem)
!SLIDE
# Starts a Rack server for running Jasmine against your code
!SLIDE
# Really easy to plug into an existing Rails project
!SLIDE
# Want to make that run fast?
!SLIDE
# Use PhantomJS or `jasmine-headless-webkit`
!SLIDE
# Fast code running in a real browser
!SLIDE
# Evergreen
!SLIDE
# Jasminerice
!SLIDE
# Node.js
!SLIDE
# Pick your favorite!

View File

@ -0,0 +1,141 @@
!SLIDE
# Some miscellaneous hints and tips
!SLIDE
# Testing jQuery
!SLIDE
# Mocking and stubbing `$.fn` calls
!SLIDE even-larger
``` coffeescript
this.containerWaiter = ->
$('#container').
addClass('wait').
append('<div class="waiting" />')
```
!SLIDE even-larger
``` coffeescript
$.fn.makeWait = ->
$(this).
addClass('wait').
append('<div class="waiting" />')
this
```
!SLIDE even-larger
``` coffeescript
this.containerWaiter = ->
$('#container').makeWait()
```
!SLIDE
# `jquery-jasmine`
!SLIDE even-larger
``` coffeescript
describe 'container', ->
beforeEach ->
setFixtures('<div id="container" />')
it 'should make it wait', ->
containerWaiter()
expect($('#container')).
toHaveClass('wait')
expect($('#container')).
toContain('div.waiting')
```
!SLIDE image-80-percent
<img src="assets/wet-cat.jpg" />
!SLIDE image-80-percent
<img src="assets/spaghetti.jpg" />
!SLIDE even-larger
``` coffeescript
describe '$.fn.makeWait', ->
it 'should make wait', ->
$div = $('<div />')
$div.makeWait()
expect($div).toHaveClass('wait')
expect($div).toContain('div.waiting')
```
!SLIDE even-larger
``` coffeescript
describe 'container', ->
beforeEach ->
setFixtures('<div id="container" />')
spyOn($.fn, 'makeWait')
it 'should make it wait', ->
containerWaiter()
expect($.fn.makeWait).
toHaveBeenCalled()
```
!SLIDE
# No longer testing jQuery, just testing for our code
!SLIDE
# Animations and other time-dependent things
!SLIDE even-larger
``` coffeescript
class @Cat
constructor: ->
@mood = "happy"
pet: ->
setTimeout(
-> @mood = "angry"
, 500
)
```
!SLIDE
# Do you really need to test the `setTimeout`?
!SLIDE even-larger
``` coffeescript
class Cat
constructor: ->
@mood = "happy"
pet: -> setTimeout(@makeAngry, 500)
makeAngry: => @mood = "angry"
```
!SLIDE
# Use Jasmine's `waitsFor` and `runs`
!SLIDE larger
``` coffeescript
describe 'cat moods', ->
it 'should change moods', ->
cat = new Cat()
# we want to know the cat's current mood
currentMood = cat.mood
# start petting the cat
runs -> cat.pet()
# wait one second for the cat's mood to change
waitsFor(
->
cat.mood != currentMood
, "Cat changed its mood",
1000
)
# expect the inevitable
runs ->
expect(cat.mood).toEqual('angry')
```

View File

@ -0,0 +1,48 @@
!SLIDE
# So that's pretty much it.
!SLIDE even-larger
# Basic parts of Jasmine unit tests
``` coffeescript
describe
it
expect
toSomething()
beforeEach
afterEach
```
!SLIDE even-larger
# Mocking and stubbing
``` coffeescript
direct method replacement
spyOn()
toHaveBeenCalled()
toHaveBeenCalledWith()
```
!SLIDE
# Running Jasmine in your project
!SLIDE even-larger
# Hints and tips for JavaScript testing
``` coffeescript
waitsFor()
runs()
```
!SLIDE
## [Jasmine documentation](http://pivotal.github.com/jasmine/)
!SLIDE
## [johnbintz.github.com/tea-time](http://johnbintz.github.com/tea-time/)
!SLIDE
# Any questions?
!SLIDE
# Thank you!

2
views/_header.haml Normal file
View File

@ -0,0 +1,2 @@
%link{:href => 'http://fonts.googleapis.com/css?family=Acme', :rel => 'stylesheet', :type => 'text/css'}/