Slide 1

Slide 1 text

Let's Make Testing Fun Again Noel Rappin Thursday, September 6, 12

Slide 2

Slide 2 text

Who is this guy? Thursday, September 6, 12

Slide 3

Slide 3 text

Kent Beck Thursday, September 6, 12

Slide 4

Slide 4 text

Thursday, September 6, 12

Slide 5

Slide 5 text

1998 Thursday, September 6, 12

Slide 6

Slide 6 text

In 2001, this was my RSpec Thursday, September 6, 12

Slide 7

Slide 7 text

Thursday, September 6, 12

Slide 8

Slide 8 text

Avdi Grimm, "Confident Ruby" Thursday, September 6, 12

Slide 9

Slide 9 text

"This talk is fundamentally about joy." Thursday, September 6, 12

Slide 10

Slide 10 text

Do we still love writing tests? Thursday, September 6, 12

Slide 11

Slide 11 text

Or do we just love having written tests? Thursday, September 6, 12

Slide 12

Slide 12 text

Or do we just love saying that we've written tests? Thursday, September 6, 12

Slide 13

Slide 13 text

What makes testing fun? Thursday, September 6, 12

Slide 14

Slide 14 text

Feedback Thursday, September 6, 12

Slide 15

Slide 15 text

Communication Thursday, September 6, 12

Slide 16

Slide 16 text

Given When Then Thursday, September 6, 12

Slide 17

Slide 17 text

Given When Then Setup Run Thursday, September 6, 12

Slide 18

Slide 18 text

Setup Thursday, September 6, 12

Slide 19

Slide 19 text

Name accurately Thursday, September 6, 12

Slide 20

Slide 20 text

describe User do it "handles true input" do # ... end it "handles false input" do # ... end end Bad: Lazy Thursday, September 6, 12

Slide 21

Slide 21 text

describe User do describe "check_permissions" do it "validates permissions" do # ... end it "invalidates no permissions" do # ... end end end Better: Structure by Method Thursday, September 6, 12

Slide 22

Slide 22 text

describe User do describe "when checking for validations" do it "allows an admin user access" do # ... end it "denies a regular user access" do # ... end end end Best: Name by Behavior Thursday, September 6, 12

Slide 23

Slide 23 text

Take a small, exact step Thursday, September 6, 12

Slide 24

Slide 24 text

describe User do it "checks for validations" do user = User.new(:role => "admin") post = Post.new(:author => user) user.should be_able_to_see_post(post) user.role = "user" user.should be_able_to_see_post(post) post.author = User.new user.should_not be_able_to_see_post(post) end end Bad: Trying to do too much Thursday, September 6, 12

Slide 25

Slide 25 text

describe User do it "allows an admin to see a post" do user = User.new(:role => "admin") post = Post.new(:author => user) user.should be_able_to_see_post(post) end it "allows a user to see their own post" user = User.new(:role => "user") post = Post.new(:author => user) user.should be_able_to_see_post(post) end end Better: one at a time Thursday, September 6, 12

Slide 26

Slide 26 text

Given Thursday, September 6, 12

Slide 27

Slide 27 text

Minimize object use Thursday, September 6, 12

Slide 28

Slide 28 text

describe User do it "finds users who have high ratings" do (1 .. 10).each do |rating| User.create(:rating => rating, :name => "User #{rating}") end User.high_rating.map(&:name).should =~ ["User 9", "User 10"] end end Bad: Too many objects Thursday, September 6, 12

Slide 29

Slide 29 text

Better: Just enough objects describe User do it "finds users who have high ratings" do high = User.new(:rating => 9) low = User.new(:rating => 8) User.high_rating.should =~ [high] end end Thursday, September 6, 12

Slide 30

Slide 30 text

Clarity on object creation Thursday, September 6, 12

Slide 31

Slide 31 text

describe User do it "finds users who have high ratings" do high = User.new(:rating => 9, :password => "test", :password_confirmation => "test", :username => "test", :email => "[email protected]") low = User.new(:rating => 8, :password => "test", :password_confirmation => "test", :username => "test", :email => "[email protected]") User.high_rating.should =~ [high] end end Bad: Lots of noise Thursday, September 6, 12

Slide 32

Slide 32 text

Better: Important stuff visible describe User do it "finds users who have high ratings" do high = Factory.build(:user, :rating => 9) low = Factory.build(:user, :rating => 8) User.high_rating.map(&:name).should =~ [high] end end Thursday, September 6, 12

Slide 33

Slide 33 text

Keep relevant setup close Thursday, September 6, 12

Slide 34

Slide 34 text

describe User do before(:each) do @user = Factory.build(:user, :name => "Fred") end #then some stuff Thursday, September 6, 12

Slide 35

Slide 35 text

#and more stuff #and more stuff Thursday, September 6, 12

Slide 36

Slide 36 text

it "uses the user" do @user.name.should == # i don't remember end end Thursday, September 6, 12

Slide 37

Slide 37 text

When Thursday, September 6, 12

Slide 38

Slide 38 text

Test one action at a time Thursday, September 6, 12

Slide 39

Slide 39 text

Scenario: User logs in Given I am a logged in user When I hit my home page Then I see my profile When I go to edit my profile Then my profile is updated When I go to see my content Then my content is available Thursday, September 6, 12

Slide 40

Slide 40 text

Background: Given I am a logged in user Scenario: User logs in When I hit my home page Then I see my profile Scenario: User edits profile When I go to edit my profile Then my profile is updated Thursday, September 6, 12

Slide 41

Slide 41 text

Invoke the action at the right level Thursday, September 6, 12

Slide 42

Slide 42 text

Cucumber makes a lousy unit test framework Thursday, September 6, 12

Slide 43

Slide 43 text

Then Thursday, September 6, 12

Slide 44

Slide 44 text

Single Assertion Thursday, September 6, 12

Slide 45

Slide 45 text

describe "historical type behavior" do before(:each) do user.deals_for(:restaurant, 2, 5) deal.types << :restaurant end it "knows the pct of types for restaurant" do user.pct_bought_for(:restaurant).should == 0.4 end it "knows the score for each restaurant type" do user.history_score_for(:restaurant).should == 90 end end Single assertion Thursday, September 6, 12

Slide 46

Slide 46 text

Single test describe "historical type behavior" do it "calulates deal percentages" user.deals_seen_for(:restaurant, 2, 5) deal.types << :restaurant user.pct_bought_for(:restaurant).should == 0.4 user.history_score_for(:restaurant).should == 90 end end Thursday, September 6, 12

Slide 47

Slide 47 text

Test the right thing Thursday, September 6, 12

Slide 48

Slide 48 text

describe UserController do describe "get index" do it "searches properly" do @fred = User.create(:name => "Fred") @barney = User.create(:name => "Barney") get :index, :search => "Fred" assigns(:user).should == [@fred] end end end Thursday, September 6, 12

Slide 49

Slide 49 text

Test Simple Values Thursday, September 6, 12

Slide 50

Slide 50 text

describe User do it "searches properly" do @fred = User.create(:name => "Fred") @barney = User.create(:name => "Barney") result = User.search("Fred") result.should == [@fred] end end Using ActiveRecord in check Thursday, September 6, 12

Slide 51

Slide 51 text

describe User do it "searches properly" do @fred = User.create(:name => "Fred") @barney = User.create(:name => "Barney") result = User.search("Fred") result.map(&:name).should == ["Fred"] end end Using literal in check Thursday, September 6, 12

Slide 52

Slide 52 text

Spy, don't mock Thursday, September 6, 12

Slide 53

Slide 53 text

var cheeseburger = { cheeses: function() { // Ajax call to cheese server }; }; it('spies on the cheese server', function() { spyOn(cheeseburger, 'cheeses'); cheeseburger.cheeses(); expect(cheeseburger.cheeses).toHaveBeenCalled(); }); Thursday, September 6, 12

Slide 54

Slide 54 text

Magic literal testing Thursday, September 6, 12

Slide 55

Slide 55 text

it "averages ratings" do vendor.add_ratings(3) deal.regular_price = 16.00 deal.sale_price = 12.00 user.relevance_of(deal).should == 42.5 end Thursday, September 6, 12

Slide 56

Slide 56 text

it "averages ratings" do vendor.add_ratings(3) deal.regular_price = 16.00 deal.sale_price = 12.00 user.relevance_of(deal).should == (60 + (12.0 / 16)) / 2 end Thursday, September 6, 12

Slide 57

Slide 57 text

Double sided booleans Thursday, September 6, 12

Slide 58

Slide 58 text

Running Thursday, September 6, 12

Slide 59

Slide 59 text

Reduce Friction Thursday, September 6, 12

Slide 60

Slide 60 text

Display clearly Thursday, September 6, 12

Slide 61

Slide 61 text

After Thursday, September 6, 12

Slide 62

Slide 62 text

Listen to your tests Thursday, September 6, 12

Slide 63

Slide 63 text

Refactoring Thursday, September 6, 12

Slide 64

Slide 64 text

Don't Shortcut the Process Thursday, September 6, 12

Slide 65

Slide 65 text

Avoid the Uncanny Testing Valley Thursday, September 6, 12

Slide 66

Slide 66 text

Developer testing is a means to an end Thursday, September 6, 12

Slide 67

Slide 67 text

@noelrap http://www.noelrappin.com/mstwjs WCR_25 Thursday, September 6, 12