SlideShare uma empresa Scribd logo
1 de 42
Test First Teaching




Alex Chaffee
@alexch
Sarah Allen
@ultrasaurus
Why should you care?

•   you want to learn Ruby
•   you want to improve your Ruby skills
•   you have a friend or colleague who wants to
    learn Ruby
•   you want to help us improve our materials
•   by teaching, you learn...
No, seriously:
by teaching, you learn!
•   the best engineers are good teachers
•   we live and work in collaborative
    environments
•   it is not enough to know any thing well
•   we must teach in order to effectively produce
    software
What is
        Test-First Teaching?
•   teacher provides microtests
•   student makes them pass
    •   one test at a time
•   can be used guided (in classroom) or solo
    •   or with a pair
Pairing Is Teaching
Pairing in the classroom




   •   students learn together and teach each other
   •   each pair can proceed through exercises at
       their own pace
   •   teacher is freed to wander the room
How do we know it's a
            good idea?
                                  2002 Alex Chaffee
                                       jGuru Java curriculum
                                  2005 Mike Clark
                 many                  Ruby Learning Tests
          independent             2006 ara.t.howard
             inventors                 Ruby Quiz #67
                                       "Metakoans"
                                  2008 Yehuda Katz
                                       & Matt Aimonetti
                                       Ruby on Rails training
                                  Who else?
http://www.flickr.com/photos/annais/9335897/sizes/z/
How do we know it's a
     good idea?


it works
Learning Ruby via Tests

•   [Test-First Teaching](http://testfirst.org)
    by Sarah Allen and Alex Chaffee
•   [Ruby Koans](http://rubykoans.com)
    by Jim Weirich and Joe O’Brien
•   [Metakoans](http://rubyquiz.com/quiz67.html)
    by ara.t.howard
Other Guided Learning
•   [ruby-warrior](http://github.com/ryanb/ruby-warrior) by Ryan
    Bates - a game written in Ruby for learning Ruby

•   [Try Ruby](http://tryruby.org) runs a Ruby interpreter in your
    browser, with hints and advice

•   [Growing OO Software In Ruby](http://www.exampler.com/
    blog/2009/12/17/growing-object-oriented-software-in-ruby/) by
    Brian Marick

    •   Ruby version of [Growing Object-Oriented Software
        Guided by Tests](http://www.growing-object-oriented-
        software.com/)
Created by:
                                              Sarah Allen
                                              Alex Chaffee
                                              Liah Hansen
                                              and friends
       Test-First Teaching....
http://testfirst.org
             Maybe we should call it Test-First Learning
http://github.com/ultrasaurus/test-first-teaching
Traditional Professional
              Programming Classes
                                                 Big, Boring Lecture
                                                 Followed by Exercises
                                                  • multiple choice
                                                  • fill in the blanks with words
                                                      or pseudocode
                                                  •   skeleton code - big program
                                                      with chunks excised and
                                                      replaced with comments
                                                  •   large task - soup to nuts
                                                      without feedback
.flickr.com/photos/chasephotography/3890300709/
writing code
 is engaging
Methodology

•   Run the test
•   Watch it fail
•   Write code to fix the first failure
•   See it pass
•   Refactor

    Sound familiar?
Why TFT?
•   makes the assignment very clear
•   student gets immediate feedback on progress
    (or lack thereof)
•   removes the magic
    •   leads the student through all the steps to
        writing the code
•   teaches student to read errors
Embrace Failure
Embrace Failure

•   start from a point of failure
    •   it feels like it's not your fault
•   people learn better when they're not stressed
•   playfulness enhances learning
In a classroom setting,
           do more...
•   Conceptual Overview
•   Experimentation (Play, irb)
•   Live Coding a Real-World Example
•   Simple hands-on exercise
•   Name what they learned
TFT Examples



        Let's look at some code
Arithmetic
require "calculator"

describe Calculator do

  before do
    @calculator = Calculator.new
  end

  it "adds 0 and 0" do
    @calculator.add(0,0).should == 0
  end

  it "adds 2 and 2" do
    @calculator.add(2,2).should == 4
  end

  it "adds positive numbers" do
    @calculator.add(2,6).should == 8
  end

  it "subtracts numbers" do
    @calculator.subtract(10,4).should == 6
  end
end
require "pig_latin"

describe "#translate" do
                                   Strings
  include PigLatinTranslator

  it "should translate a simple word" do
    s = translate("nix")
    s.should == "ixnay"
  end

  it "should translate a word beginning with a vowel" do
    s = translate("apple")
    s.should == "appleay"
  end

  it "should translate a word with two consonants" do
    s = translate("stupid")
    s.should == "upidstay"
  end

  it "should translate two words" do
    s = translate("eat pie")
    s.should == "eatay iepay"
  end

  it "should translate many words" do
    s = translate("the quick brown fox")
    s.should == "ethay ickquay ownbray oxfay"
  end
end
Pig Latin Solution
module PigLatinTranslator
  def translate(s)
    s.split.map do |word|
      v = first_vowel(word)
      word.slice(v..-1) + word[0,v] + "ay"
    end.join(" ")
  end

  def first_vowel(word)
    if word =~ /^qu/
      2
    else
      word.gsub(/[aeiou].*$/, '').size
    end
  end
end
Another
          Pig Latin Solution
module PigLatinTranslator
  def translate(s)
    words = s.split
      s = words.map do |s|
      l = s.length
        if /^[aeiou]/ .match(s)
            s + "ay"
        elsif /^qu/ .match(s[0..1])
             s[2..(l+1)] + s[0..1] + "ay"
        elsif /[aeiou]/ .match(s[1..1])
             s[1..(l+1)] + s[0..0] + "ay"
        else
             s[2..(l+1)] + s[0..1] + "ay"
        end
      end
      s = s.join(" ")
  end
end
And Another
             Pig Latin Solution
module PigLatinTranslator
  def translate(word)
    words = word.split(" ")
    arrResult = []
    words.each do |word|
      m = word.match(/^(qu)*[^aeiou]*/)
      if(m.nil?)
        arrResult << add_ay(word)
      else
        arrResult << add_ay(m.post_match + m.to_s)
      end
    end
    arrResult.join(" ")
  end

  def add_ay(word)
    word + "ay"
  end
end
Iterators
describe Calculator do

  before do
    @calculator = Calculator.new
  end

  describe "#sum" do
    it "computes the sum of an empty array" do
      @calculator.sum([]).should == 0
    end

    it "computes the sum of an array of one number" do
      @calculator.sum([7]).should == 7
    end

    it "computes the sum of an array of two numbers" do
      @calculator.sum([7,11]).should == 18
    end

    it "computes the sum of an array of many numbers" do
      @calculator.sum([1,3,5,7,9]).should == 25
    end
  end
Iterators
require "array_extension"

describe Array do

  describe "#sum" do
    it "should be 0 for an empty array" do
      [].sum.should == 0
    end

    it "should add all of the elements" do
      [1,2,4].sum.should == 7
    end
  end

end


            (and open classes)
TDD Extra Credit!
  # Test-Driving Bonus: once the above tests pass,
  # write tests and code for the following:

  it "multiplies two numbers"

  it "multiplies an array of numbers"

  it "raises one number to the power of another number"

  # http://en.wikipedia.org/wiki/Factorial
  describe "#factorial" do
    it "computes the factorial of 0"
    it "computes the factorial of 1"
    it "computes the factorial of 2"
    it "computes the factorial of 5"
    it "computes the factorial of 10"
  end

end
But...
  that's
impossible
Solutions for
 Challenging
   Idioms
        blocks
         time
   method missing
   builder pattern
Blocks
require "performance_monitor"
                                  (and mocks)             it "takes exactly 1 second to run a block that
describe PerformanceMonitor do                          sleeps for 1 second (with stubs)" do
  before do                                                 fake_time = 100
    @monitor = PerformanceMonitor.new                       Time.stub!(:now).and_return {fake_time}
  end                                                       @monitor.run do
                                                              fake_time += 1
  it "takes about 0 seconds to run an empty block" do       end.should == 1
    @monitor.run do                                       end
    end.should be_close(0, 0.1)
  end                                                     it "runs a block N times" do
                                                            n = 0
  it "takes exactly 0 seconds to run an empty block         @monitor.run(4) do
(with stubs)" do                                              n += 1
    Time.stub!(:now).and_return(100)                        end
    @monitor.run do                                         n.should == 4
    end.should == 0                                       end
  end
                                                          it "returns the average time, not the total time,
  it "takes about 1 second to run a block that sleeps   when running multiple times" do
for 1 second" do                                            run_times = [8,6,5,7]
    @monitor.run do                                         run_index = 0
      sleep 1                                               fake_time = 100
    end.should be_close(1, 0.1)                             Time.stub(:now).and_return { fake_time }
  end                                                       @monitor.run(4) do
                                                              fake_time += run_times[run_index]
                                                              run_index += 1
                                                            end.should == 6
                                                          end

                                                        end
method_missing, nested
       closures, and the builder pattern
require "xml_document"
                                                       it "nests several levels" do
describe XmlDocument do                                  @xml.hello do
  before do                                                @xml.goodbye do
    @xml = XmlDocument.new                                   @xml.come_back do
  end                                                          @xml.ok_fine(:be => "that_way")
                                                             end
  it "renders an empty tag" do                             end
    @xml.hello.should == "<hello/>"                      end.should ==
  end                                                "<hello><goodbye><come_back><ok_fine be='that_way'/
                                                     ></come_back></goodbye></hello>"
  it "renders a tag with attributes" do                end
    @xml.hello(:name => 'dolly').should == "<hello
name='dolly'/>"                                        it "indents" do
  end                                                    @xml = XmlDocument.new(true)
                                                         @xml.hello do
  it "renders a randomly named tag" do                     @xml.goodbye do
    tag_name = (1..8).map{|i|                                @xml.come_back do
('a'..'z').to_a[rand(26)]}.join                                 @xml.ok_fine(:be => "that_way")
    @xml.send(tag_name).should == "<#{tag_name}/>"           end
  end                                                      end
                                                         end.should ==
  it "renders block with text inside" do                 "<hello>n" +
    @xml.hello do                                        " <goodbye>n" +
      "dolly"                                            "     <come_back>n" +
    end.should == "<hello>dolly</hello>"                 "       <ok_fine be='that_way'/>n" +
  end                                                    "     </come_back>n" +
                                                         " </goodbye>n" +
  it "nests one level" do                                "</hello>n"
    @xml.hello do                                      end
      @xml.goodbye                                   end
    end.should == "<hello><goodbye/></hello>"
  end
threads
               (sorry for the Java)
public void testThreadSafe() throws InterruptedException
{
    int DEPOSITORS = 50;
    int AMOUNT = 2;
    // note: increase this value until it *fails* on your CPU.
    // Then fix it.
    int REPS = 25000;
    Account account = new Account("Joe", 0);
    Thread[] depositors = new Thread[DEPOSITORS];
    for (int i=0; i< DEPOSITORS; ++i) {
        depositors[i] = new Depositor(account, AMOUNT, REPS);
        depositors[i].start();
    }
    for (int i=0; i< DEPOSITORS; ++i) {
        depositors[i].join();
    }
    assertEquals(REPS * DEPOSITORS * AMOUNT, account.getBalance());
}
ruby koans


•   self-guided, test-driven
•   Ruby language basics
•   very fun, whimsical and elegant
ruby koans example
require File.expand_path(File.dirname(__FILE__) + '/edgecase')

class AboutStrings < EdgeCase::Koan
  def test_double_quoted_strings_are_strings
    string = "Hello, World"
                                                                    usually self-
    assert_equal __, string.is_a?(String)
  end                                                               contained
  def test_single_quoted_strings_are_also_strings
                                                                    just tests and fixtures,
    string = 'Goodbye, World'                                       with no class declaration
    assert_equal __, string.is_a?(String)
  end

  def test_use_single_quotes_to_create_string_with_double_quotes
                                                                    “fill in the
    string = 'He said, "Go Away."'
    assert_equal __, string                                         blanks”
  end
                                                                    technique
  def test_use_double_quotes_to_create_strings_with_single_quotes
    string = "Don't"
    assert_equal __, string
  end
                                                                    teaching through
  def test_use_backslash_for_those_hard_cases                       practice and
    a = "He said, "Don't""
    b = 'He said, "Don't"'                                         challenge
    assert_equal __, a == b
  end
TFT != TDD
•   Mechanics of testing are hard to learn
•   TFT teaches programming; TDD is design
•   At the end of some modules,
    students write their own tests for “extra
    credit”
    •   doesn’t really flex the creative muscles
        required for software design
What about TDD?
•   easier to learn TDD, post-TFT
    •   know the language
    •   know the test framework
    •   used to the rhythm of test-first
•   study design patterns, or check out [GOOS]
    (http://www.exampler.com/blog/2009/12/17/
    growing-object-oriented-software-in-ruby).
Credits
•   Mr. Clean® is a registered trademark of Procter &
    Gamble, used without permission
    •   Parody is fair use!
•   Fail Whale illustrated by Yiying Lu (http://
    www.yiyinglu.com/)
•   Pair Programming photos by Lee Lundrigan
•   Thank you Flickr and Creative Commons
    (see slides for attribution)
Sarah

Blazing Cloud
Alex
          alexch.github.com
          @alexch
Erector

Moodlog

Cohuman

 Wrong
Learning should be fun

•   Questions?

Mais conteúdo relacionado

Mais de Sarah Allen

Improving Federal Government Services
Improving Federal Government ServicesImproving Federal Government Services
Improving Federal Government ServicesSarah Allen
 
Transparency Wins
Transparency WinsTransparency Wins
Transparency WinsSarah Allen
 
A Short History of Computers
A Short History of ComputersA Short History of Computers
A Short History of ComputersSarah Allen
 
Making Software Fun
Making Software FunMaking Software Fun
Making Software FunSarah Allen
 
Power of Transparency
Power of TransparencyPower of Transparency
Power of TransparencySarah Allen
 
Designing for Fun
Designing for FunDesigning for Fun
Designing for FunSarah Allen
 
Ruby in the US Government for Ruby World Conference
Ruby in the US Government for Ruby World ConferenceRuby in the US Government for Ruby World Conference
Ruby in the US Government for Ruby World ConferenceSarah Allen
 
Identities of Dead People
Identities of Dead PeopleIdentities of Dead People
Identities of Dead PeopleSarah Allen
 
3 Reasons Not to Use Ruby
3 Reasons Not to Use Ruby 3 Reasons Not to Use Ruby
3 Reasons Not to Use Ruby Sarah Allen
 
Ruby Nation: Why no haz Ruby?
Ruby Nation: Why no haz Ruby?Ruby Nation: Why no haz Ruby?
Ruby Nation: Why no haz Ruby?Sarah Allen
 
Why no ruby in gov?
Why no ruby in gov?Why no ruby in gov?
Why no ruby in gov?Sarah Allen
 
People Patterns or What I learned from Toastmasters
People Patterns or What I learned from ToastmastersPeople Patterns or What I learned from Toastmasters
People Patterns or What I learned from ToastmastersSarah Allen
 
Blazing Cloud: Agile Product Development
Blazing Cloud: Agile Product DevelopmentBlazing Cloud: Agile Product Development
Blazing Cloud: Agile Product DevelopmentSarah Allen
 
Crowdsourced Transcription Landscape
Crowdsourced Transcription LandscapeCrowdsourced Transcription Landscape
Crowdsourced Transcription LandscapeSarah Allen
 
Lessons Learned Future Thoughts
Lessons Learned Future ThoughtsLessons Learned Future Thoughts
Lessons Learned Future ThoughtsSarah Allen
 
Mobile Web Video
Mobile Web VideoMobile Web Video
Mobile Web VideoSarah Allen
 
Elementary Computer History
Elementary Computer HistoryElementary Computer History
Elementary Computer HistorySarah Allen
 
Sarah Allen Computer Science Entrepreneur
Sarah Allen Computer Science EntrepreneurSarah Allen Computer Science Entrepreneur
Sarah Allen Computer Science EntrepreneurSarah Allen
 
Crafting Software Products
Crafting Software ProductsCrafting Software Products
Crafting Software ProductsSarah Allen
 

Mais de Sarah Allen (20)

Improving Federal Government Services
Improving Federal Government ServicesImproving Federal Government Services
Improving Federal Government Services
 
Transparency Wins
Transparency WinsTransparency Wins
Transparency Wins
 
A Short History of Computers
A Short History of ComputersA Short History of Computers
A Short History of Computers
 
Making Software Fun
Making Software FunMaking Software Fun
Making Software Fun
 
Power of Transparency
Power of TransparencyPower of Transparency
Power of Transparency
 
Designing for Fun
Designing for FunDesigning for Fun
Designing for Fun
 
Ruby in the US Government for Ruby World Conference
Ruby in the US Government for Ruby World ConferenceRuby in the US Government for Ruby World Conference
Ruby in the US Government for Ruby World Conference
 
Identities of Dead People
Identities of Dead PeopleIdentities of Dead People
Identities of Dead People
 
Let's pretend
Let's pretendLet's pretend
Let's pretend
 
3 Reasons Not to Use Ruby
3 Reasons Not to Use Ruby 3 Reasons Not to Use Ruby
3 Reasons Not to Use Ruby
 
Ruby Nation: Why no haz Ruby?
Ruby Nation: Why no haz Ruby?Ruby Nation: Why no haz Ruby?
Ruby Nation: Why no haz Ruby?
 
Why no ruby in gov?
Why no ruby in gov?Why no ruby in gov?
Why no ruby in gov?
 
People Patterns or What I learned from Toastmasters
People Patterns or What I learned from ToastmastersPeople Patterns or What I learned from Toastmasters
People Patterns or What I learned from Toastmasters
 
Blazing Cloud: Agile Product Development
Blazing Cloud: Agile Product DevelopmentBlazing Cloud: Agile Product Development
Blazing Cloud: Agile Product Development
 
Crowdsourced Transcription Landscape
Crowdsourced Transcription LandscapeCrowdsourced Transcription Landscape
Crowdsourced Transcription Landscape
 
Lessons Learned Future Thoughts
Lessons Learned Future ThoughtsLessons Learned Future Thoughts
Lessons Learned Future Thoughts
 
Mobile Web Video
Mobile Web VideoMobile Web Video
Mobile Web Video
 
Elementary Computer History
Elementary Computer HistoryElementary Computer History
Elementary Computer History
 
Sarah Allen Computer Science Entrepreneur
Sarah Allen Computer Science EntrepreneurSarah Allen Computer Science Entrepreneur
Sarah Allen Computer Science Entrepreneur
 
Crafting Software Products
Crafting Software ProductsCrafting Software Products
Crafting Software Products
 

Test First Teaching - GoGaRuCo 2010

  • 1. Test First Teaching Alex Chaffee @alexch Sarah Allen @ultrasaurus
  • 2. Why should you care? • you want to learn Ruby • you want to improve your Ruby skills • you have a friend or colleague who wants to learn Ruby • you want to help us improve our materials • by teaching, you learn...
  • 3. No, seriously: by teaching, you learn! • the best engineers are good teachers • we live and work in collaborative environments • it is not enough to know any thing well • we must teach in order to effectively produce software
  • 4. What is Test-First Teaching? • teacher provides microtests • student makes them pass • one test at a time • can be used guided (in classroom) or solo • or with a pair
  • 6. Pairing in the classroom • students learn together and teach each other • each pair can proceed through exercises at their own pace • teacher is freed to wander the room
  • 7. How do we know it's a good idea? 2002 Alex Chaffee jGuru Java curriculum 2005 Mike Clark many Ruby Learning Tests independent 2006 ara.t.howard inventors Ruby Quiz #67 "Metakoans" 2008 Yehuda Katz & Matt Aimonetti Ruby on Rails training Who else? http://www.flickr.com/photos/annais/9335897/sizes/z/
  • 8. How do we know it's a good idea? it works
  • 9. Learning Ruby via Tests • [Test-First Teaching](http://testfirst.org) by Sarah Allen and Alex Chaffee • [Ruby Koans](http://rubykoans.com) by Jim Weirich and Joe O’Brien • [Metakoans](http://rubyquiz.com/quiz67.html) by ara.t.howard
  • 10. Other Guided Learning • [ruby-warrior](http://github.com/ryanb/ruby-warrior) by Ryan Bates - a game written in Ruby for learning Ruby • [Try Ruby](http://tryruby.org) runs a Ruby interpreter in your browser, with hints and advice • [Growing OO Software In Ruby](http://www.exampler.com/ blog/2009/12/17/growing-object-oriented-software-in-ruby/) by Brian Marick • Ruby version of [Growing Object-Oriented Software Guided by Tests](http://www.growing-object-oriented- software.com/)
  • 11. Created by: Sarah Allen Alex Chaffee Liah Hansen and friends Test-First Teaching.... http://testfirst.org Maybe we should call it Test-First Learning http://github.com/ultrasaurus/test-first-teaching
  • 12. Traditional Professional Programming Classes Big, Boring Lecture Followed by Exercises • multiple choice • fill in the blanks with words or pseudocode • skeleton code - big program with chunks excised and replaced with comments • large task - soup to nuts without feedback .flickr.com/photos/chasephotography/3890300709/
  • 13. writing code is engaging
  • 14. Methodology • Run the test • Watch it fail • Write code to fix the first failure • See it pass • Refactor Sound familiar?
  • 15.
  • 16. Why TFT? • makes the assignment very clear • student gets immediate feedback on progress (or lack thereof) • removes the magic • leads the student through all the steps to writing the code • teaches student to read errors
  • 18. Embrace Failure • start from a point of failure • it feels like it's not your fault • people learn better when they're not stressed • playfulness enhances learning
  • 19. In a classroom setting, do more... • Conceptual Overview • Experimentation (Play, irb) • Live Coding a Real-World Example • Simple hands-on exercise • Name what they learned
  • 20. TFT Examples Let's look at some code
  • 21. Arithmetic require "calculator" describe Calculator do before do @calculator = Calculator.new end it "adds 0 and 0" do @calculator.add(0,0).should == 0 end it "adds 2 and 2" do @calculator.add(2,2).should == 4 end it "adds positive numbers" do @calculator.add(2,6).should == 8 end it "subtracts numbers" do @calculator.subtract(10,4).should == 6 end end
  • 22.
  • 23. require "pig_latin" describe "#translate" do Strings include PigLatinTranslator it "should translate a simple word" do s = translate("nix") s.should == "ixnay" end it "should translate a word beginning with a vowel" do s = translate("apple") s.should == "appleay" end it "should translate a word with two consonants" do s = translate("stupid") s.should == "upidstay" end it "should translate two words" do s = translate("eat pie") s.should == "eatay iepay" end it "should translate many words" do s = translate("the quick brown fox") s.should == "ethay ickquay ownbray oxfay" end end
  • 24. Pig Latin Solution module PigLatinTranslator def translate(s) s.split.map do |word| v = first_vowel(word) word.slice(v..-1) + word[0,v] + "ay" end.join(" ") end def first_vowel(word) if word =~ /^qu/ 2 else word.gsub(/[aeiou].*$/, '').size end end end
  • 25. Another Pig Latin Solution module PigLatinTranslator def translate(s) words = s.split s = words.map do |s| l = s.length if /^[aeiou]/ .match(s) s + "ay" elsif /^qu/ .match(s[0..1]) s[2..(l+1)] + s[0..1] + "ay" elsif /[aeiou]/ .match(s[1..1]) s[1..(l+1)] + s[0..0] + "ay" else s[2..(l+1)] + s[0..1] + "ay" end end s = s.join(" ") end end
  • 26. And Another Pig Latin Solution module PigLatinTranslator def translate(word) words = word.split(" ") arrResult = [] words.each do |word| m = word.match(/^(qu)*[^aeiou]*/) if(m.nil?) arrResult << add_ay(word) else arrResult << add_ay(m.post_match + m.to_s) end end arrResult.join(" ") end def add_ay(word) word + "ay" end end
  • 27. Iterators describe Calculator do before do @calculator = Calculator.new end describe "#sum" do it "computes the sum of an empty array" do @calculator.sum([]).should == 0 end it "computes the sum of an array of one number" do @calculator.sum([7]).should == 7 end it "computes the sum of an array of two numbers" do @calculator.sum([7,11]).should == 18 end it "computes the sum of an array of many numbers" do @calculator.sum([1,3,5,7,9]).should == 25 end end
  • 28. Iterators require "array_extension" describe Array do describe "#sum" do it "should be 0 for an empty array" do [].sum.should == 0 end it "should add all of the elements" do [1,2,4].sum.should == 7 end end end (and open classes)
  • 29. TDD Extra Credit! # Test-Driving Bonus: once the above tests pass, # write tests and code for the following: it "multiplies two numbers" it "multiplies an array of numbers" it "raises one number to the power of another number" # http://en.wikipedia.org/wiki/Factorial describe "#factorial" do it "computes the factorial of 0" it "computes the factorial of 1" it "computes the factorial of 2" it "computes the factorial of 5" it "computes the factorial of 10" end end
  • 31. Solutions for Challenging Idioms blocks time method missing builder pattern
  • 32. Blocks require "performance_monitor" (and mocks) it "takes exactly 1 second to run a block that describe PerformanceMonitor do sleeps for 1 second (with stubs)" do before do fake_time = 100 @monitor = PerformanceMonitor.new Time.stub!(:now).and_return {fake_time} end @monitor.run do fake_time += 1 it "takes about 0 seconds to run an empty block" do end.should == 1 @monitor.run do end end.should be_close(0, 0.1) end it "runs a block N times" do n = 0 it "takes exactly 0 seconds to run an empty block @monitor.run(4) do (with stubs)" do n += 1 Time.stub!(:now).and_return(100) end @monitor.run do n.should == 4 end.should == 0 end end it "returns the average time, not the total time, it "takes about 1 second to run a block that sleeps when running multiple times" do for 1 second" do run_times = [8,6,5,7] @monitor.run do run_index = 0 sleep 1 fake_time = 100 end.should be_close(1, 0.1) Time.stub(:now).and_return { fake_time } end @monitor.run(4) do fake_time += run_times[run_index] run_index += 1 end.should == 6 end end
  • 33. method_missing, nested closures, and the builder pattern require "xml_document" it "nests several levels" do describe XmlDocument do @xml.hello do before do @xml.goodbye do @xml = XmlDocument.new @xml.come_back do end @xml.ok_fine(:be => "that_way") end it "renders an empty tag" do end @xml.hello.should == "<hello/>" end.should == end "<hello><goodbye><come_back><ok_fine be='that_way'/ ></come_back></goodbye></hello>" it "renders a tag with attributes" do end @xml.hello(:name => 'dolly').should == "<hello name='dolly'/>" it "indents" do end @xml = XmlDocument.new(true) @xml.hello do it "renders a randomly named tag" do @xml.goodbye do tag_name = (1..8).map{|i| @xml.come_back do ('a'..'z').to_a[rand(26)]}.join @xml.ok_fine(:be => "that_way") @xml.send(tag_name).should == "<#{tag_name}/>" end end end end.should == it "renders block with text inside" do "<hello>n" + @xml.hello do " <goodbye>n" + "dolly" " <come_back>n" + end.should == "<hello>dolly</hello>" " <ok_fine be='that_way'/>n" + end " </come_back>n" + " </goodbye>n" + it "nests one level" do "</hello>n" @xml.hello do end @xml.goodbye end end.should == "<hello><goodbye/></hello>" end
  • 34. threads (sorry for the Java) public void testThreadSafe() throws InterruptedException { int DEPOSITORS = 50; int AMOUNT = 2; // note: increase this value until it *fails* on your CPU. // Then fix it. int REPS = 25000; Account account = new Account("Joe", 0); Thread[] depositors = new Thread[DEPOSITORS]; for (int i=0; i< DEPOSITORS; ++i) { depositors[i] = new Depositor(account, AMOUNT, REPS); depositors[i].start(); } for (int i=0; i< DEPOSITORS; ++i) { depositors[i].join(); } assertEquals(REPS * DEPOSITORS * AMOUNT, account.getBalance()); }
  • 35. ruby koans • self-guided, test-driven • Ruby language basics • very fun, whimsical and elegant
  • 36. ruby koans example require File.expand_path(File.dirname(__FILE__) + '/edgecase') class AboutStrings < EdgeCase::Koan def test_double_quoted_strings_are_strings string = "Hello, World" usually self- assert_equal __, string.is_a?(String) end contained def test_single_quoted_strings_are_also_strings just tests and fixtures, string = 'Goodbye, World' with no class declaration assert_equal __, string.is_a?(String) end def test_use_single_quotes_to_create_string_with_double_quotes “fill in the string = 'He said, "Go Away."' assert_equal __, string blanks” end technique def test_use_double_quotes_to_create_strings_with_single_quotes string = "Don't" assert_equal __, string end teaching through def test_use_backslash_for_those_hard_cases practice and a = "He said, "Don't"" b = 'He said, "Don't"' challenge assert_equal __, a == b end
  • 37. TFT != TDD • Mechanics of testing are hard to learn • TFT teaches programming; TDD is design • At the end of some modules, students write their own tests for “extra credit” • doesn’t really flex the creative muscles required for software design
  • 38. What about TDD? • easier to learn TDD, post-TFT • know the language • know the test framework • used to the rhythm of test-first • study design patterns, or check out [GOOS] (http://www.exampler.com/blog/2009/12/17/ growing-object-oriented-software-in-ruby).
  • 39. Credits • Mr. Clean® is a registered trademark of Procter & Gamble, used without permission • Parody is fair use! • Fail Whale illustrated by Yiying Lu (http:// www.yiyinglu.com/) • Pair Programming photos by Lee Lundrigan • Thank you Flickr and Creative Commons (see slides for attribution)
  • 41. Alex alexch.github.com @alexch Erector Moodlog Cohuman Wrong
  • 42. Learning should be fun • Questions?

Notas do Editor

  1. TFT is not sufficient for learning, but needs to be one component of a curriculum or course of self-study.