Test First Teaching background and methodology for teaching programming using automated test frameworks, how this relates to (and can lead to learning) test-driven development
4. Why should you care?
• you want to improve your Ruby skills
• you want to do testing (better)
• you have a friend or colleague who wants to
learn Ruby
• you want to help us improve our materials
• by teaching, you learn...
5. by teaching, you learn...
• the best engineers are good teachers
• we live and work in collaborative
environments
• it is not enough to know any single thing well
• we must teach in order to effectively produce
software
6. 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
8. 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
9. 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
and many more...
http://www.flickr.com/photos/annais/9335897/sizes/z/
11. 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
12. 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/)
13. 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
14. 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/
16. Methodology
• Run the test
• Watch it fail
• Write code to fix the first failure
• See it pass
• Refactor
Sound familiar?
17. 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
19. 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
21. Objects and Methods
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. 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
26. 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
27. 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
28. 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());
}
29. ruby koans
• self-guided, test-driven
• Ruby language basics
• very fun, whimsical and elegant
30. 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
31. 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
32. 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).
47. Credits
• Fail Whale illustrated by Yiying Lu (http://
www.yiyinglu.com/)
• Pair Programming photos by Lee Lundrigan
and Sarah Allen
• Thank you Flickr and Creative Commons
(see slides for attribution)
TFT is not sufficient for learning, but needs to be one component of a curriculum or course of self-study.\n
\n
\n
\n
\n
\n
\n
\n
\n
If you apply a tight cycle of write one test, then write the code to implement that test, then write the next test, your code ends up growing organically. This often (though not always) leads to less wasted effort.\n
\n
High quality tests:&#xA0;Writing test first... you know they fail in the way you expect them to fail. &#xA0;Test last means that your tests will pass, which is occasionally a false positive. &#xA0;(pretense of test coverage) Writing tests is often seen as a chore; writing the tests first guarantees that at the end of the project you will have written a suite of unit tests (rather than leaving them until the end and possibly never getting around to it).\n