Minitest & Rails, No Pony Ryan Davis, Seattle.rb Min est ... · Windy City Rails, Chicago, IL...
Transcript of Minitest & Rails, No Pony Ryan Davis, Seattle.rb Min est ... · Windy City Rails, Chicago, IL...
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Min!est& Rails,No Pony
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Setting Expectations93 Slides in 30 minutes. ~3 spm.
Overview talk.
Not comparison nor comprehensive.
Less Technical than my norm.
http://www.zenspider.com/presentations/
TODO
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
What I'm not talking about
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
minitestvs
rspec
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
tddvs
bdd
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
state-istvs
mockist
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
I do all of the former.
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
As long as you test…
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
I do not care what you use or how you
use it
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Also not about benchmark, pride, or
mock.
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
I could easily talk for hours on minitest &
testing
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
I only have 30 minutes.
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Grab me [email protected]
@the_zenspider
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Min!est
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
What is Minitest?Replacement for ruby 1.8’s test/unit.
Originally 90 lines of code.
Available as a gem, and in ruby 1.9 & 2.0.
Meant to be small, clean, and very fast.
Provides a lot more than test/unit did.
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
6 3 Parts of Minitestrunner The heart of the machineminitest/unit TDD APIminitest/spec BDD API minitest/mock Simple mocking/stubbing APIminitest/pride IO pipelining exampleminitest/bench Abstract benchmark API
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Min!est Runner
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Every method starting with "test" is run in a
new context.
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
No Magic AllowedEven test discovery avoids ObjectSpace.
Minimal metaprogramming.
Uses plain classes and methods to do all work.
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Test randomization prevents order dependencies.
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Verbose mode prints test times in a sortable format:
% ruby -Ilib test/test_meta.rb -v | sort -k2 -t= -nr | head -3
MetaMagic#test_generate_meta_method = 0.03 s = . MetaMagic#test_complex_method = 0.02 s = .MetaMagic#test_simple_method = 0.01 s = .
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Test summary provides useful statistics:
# Running tests:
........................................................................
........................................................................
........................................................................
........................................................................
........................................................................
........................................................................
........................................................................
........................................................................
........................................................................
....................................
Finished tests in 0.510616s, 1339.5585 tests/s, 7428.2827 assertions/s.
684 tests, 3793 assertions, 0 failures, 0 errors, 0 skips
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
min!est/un!
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Test Example
Magic Free!
Simple Subclass
Simple Method
require "minitest/autorun"
class TestThingy < MiniTest::Unit::TestCase def test_do_the_thing assert_equal 42, Thingy.do_the_thing endend
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
assertions
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Positive Assertionsassertassert_blockassert_emptyassert_equalassert_in_deltaassert_in_epsilonassert_includesassert_instance_ofassert_kind_ofassert_match
assert_nilassert_operatorassert_outputassert_raisesassert_respond_toassert_sameassert_sendassert_silentassert_throws
Bold = new
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
assert_equal diffs: 1) Failure:test_failing_simple(TestSimple) [example.rb:8]:Expected: 42 Actual: 24
2) Failure:test_failing_complex(TestComplex) [example.rb:23]:--- expected+++ actual@@ -22,7 +22,7 @@ "line 22", "line 23", "line 24",- "line 25",+ "something unexpected", "line 26", "line 27", "line 28",
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Negative Assertionsrefuterefute_emptyrefute_equalrefute_in_deltarefute_in_epsilonrefute_includesrefute_instance_of
refute_kind_ofrefute_matchrefute_nilrefute_operatorrefute_respond_torefute_same
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Utility Methodspassskipcapture_io
flunkmu_ppmu_pp_for_diff
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
assert ! obj refute obj
assert collection.include? obj assert_include obj, collection
out, err = capture_io do do_somethingendassert_equal "output", outassert_equal "", err
assert_output "output", "" do do_somethingend
Why all these extra assertions?They're more expressive!
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
min!est/"ec
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Spec Examplerequire "minitest/autorun"
describe Thingy do it "must do the thing" do Thingy.do_the_thing.must_equal 42 endend
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Specs Transform:
Magic Free!
Simple Subclass
Simple Method
require "minitest/autorun"
class TestThingy < MiniTest::Unit::TestCase def test_0001_must_do_the_thing Thingy.do_the_thing.must_equal 42 endend
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
positive expectationsmust_bemust_be_close_tomust_be_emptymust_be_instance_ofmust_be_kind_ofmust_be_nilmust_be_same_asmust_be_silentmust_be_within_delta
must_be_within_epsilonmust_equalmust_includemust_matchmust_outputmust_raisemust_respond_tomust_sendmust_throw
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
negative expectationswont_bewont_be_close_towont_be_emptywont_be_instance_ofwont_be_kind_ofwont_be_nilwont_be_same_as
wont_be_within_deltawont_be_within_epsilonwont_equalwont_includewont_matchwont_respond_to
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
All for Freemust_equal is assert_equalwont_equal is refute_equal
etc.
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Extending
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
minitest/unitdef assert_palindrome obj assert_equal obj, obj.reverseend
assert_palindrome @ary
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
minitest/specdef must_be_palindrome self.must_equal self.reverseend
# or:
infect_an_assertion(:assert_palindrome, :must_be_palindrome)
@ary.must_be_palindrome
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Other Examplesassert_path_exists pathrefute_path_exists path
path.must_exist_as_path
assert_satisfied_by version, specifier
assert_dependencies expected, *specs
assert_reachable node, graph
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
minitest pluginscapybara_minitest_specminispec-metadataminitest-ansiminitest-aroundminitest-capistranominitest-capybaraminitest-chef-handlerminitest-ciminitest-colorizeminitest-contextminitest-debuggerminitest-displayminitest-emojiminitest-englishminitest-excludesminitest-filesystemminitest-firemock
minitest-great_expectationsminitest-growlminitest-implicit-subjectminitest-instrumentminitest-instrument-dbminitest-libnotifyminitest-lineminitest-macrubyminitest-matchersminitest-metadataminitest-mongoidminitest-must_notminitest-ncminitest-predicatesminitest-railsminitest-rails-capybaraminitest-reporters
minitest-should_syntaxminitest-shouldifyminitest-spec-contextminitest-spec-expectminitest-spec-magicminitest-spec-railsminitest-stub-constminitest-tagsminitest-wscolorminitest_owrapperminitest_shouldminitest_tu_shimmongoid-minitestpry-rescue
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Min!est Design Ra#onale
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Less is More
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Indirection is the Enemy
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
minitest/unit:assert_in_delta
Only 2 other methods need to be understood:assert (9) & message (6)
simple assertion is all that is needed
message takes a block to defer
calculation until an assertion fails
def assert_in_delta exp, act, delta = 0.001, msg = nil n = (exp - act).abs msg = message(msg) { "Expected #{exp} - #{act} (#{n}) to be < #{delta}" } assert delta >= n, msgend
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
minitest/spec:n.must_be_close_to m
(This page intentionally left blank)
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Turn up the test levels
Randomize Test Order
Require Proof of Success?
Fast
Test
Runn
er
Forced Parallelization
01
2
3
45
6 78
9
1011
Disallow meaningless assertions
???
test + spec
Benchmark TestsUse gem, not stdlib
Writ
e cu
stom
ass
ertio
ns
Refac
tor to
reus
able
testca
ses
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
min!est 4vs
min!est 5Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Architecture MT4
MiniTest::Unit::Guard
MiniTest::Assertions
MiniTest::Expectations
MiniTest::Unit::TestCase
MiniTest::Spec::DSL
MiniTest::Unit::LifecycleHooks
MiniTest::Unit
MiniTest::Spec
MiniTest
MiniTest::Assertion MiniTest::PrideIO
MiniTest::PrideLOLMiniTest::Skip
Minitest::BacktraceFilter Minitest::ParallelEach Minitest::Mock
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Minitest
Architecture MT5
Minitest::Reporter Minitest::Assertion
Minitest::BacktraceFilter Minitest::ParallelEach
Minitest::PrideIO
Minitest::PrideLOLMinitest::Skip Minitest::UnexpectedErrorMinitest::CompositeReporter
Minitest::Mock
Minitest::Runnable
Minitest::Guard
Minitest::Assertions
Minitest::Expectations
Minitest::Test
Minitest::Benchmark Minitest::Spec::DSL
Minitest::Test::LifecycleHooks
Minitest::BenchSpec
Minitest::Spec
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
MiniTest
Minitest
MiniTest::Unit
Minitest::Runnable
Minitest::Guard
Minitest::Assertions
Minitest::Expectations
Minitest::Test
Minitest::Benchmark Minitest::Spec::DSL
Minitest::Test::LifecycleHooks
Minitest::BenchSpec
Minitest::Spec
Minitest::Reporter Minitest::Assertion
Minitest::BacktraceFilter Minitest::ParallelEach
Minitest::PrideIO
Minitest::PrideLOLMinitest::Skip Minitest::UnexpectedErrorMinitest::CompositeReporter
Minitest::Mock
Differences
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Reporter Enhancements
hasmany
AbstractReporter
Reporter CompositeReporter
ProgressReporter StatisticsReporter
SummaryReporter
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Cal
l Flo
w M
T4
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Cal
l Flo
w M
T4
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Cal
l Flo
w M
T5N
o Managers!
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
User-Level Changess/MiniTest/Minitest/gs/Unit::TestCase/Test/g
Assertions?No changes!
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Rails
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
A$%eSupport::TestCase
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Minitest::Test
ActiveSupport::TestCase
MyThingyTest
Basic Architecture
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Per-test database transactions
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Fixtures
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Assertionsassert_notassert_blank → assert_predicate/must_beassert_presentassert_differenceassert_no_differenceassert_deprecatedassert_not_deprecated
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Declarative Formssetup do # ...end
teardown do # ...end
test “test name” do # ...end
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
test/horse_test.rbrequire 'test_helper'
class HorseTest < ActiveSupport::TestCase def trigger @trigger ||= Horse.new name: "Trigger Rogers" end
def test_name assert_equal "Trigger Rogers", trigger.name end
def test_friendship assert_equal "magic", trigger.friendship endend
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
A$ionController::TestCase
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Basic ArchitectureMinitest::Test
ActiveSupport::TestCase
MyControllerTest
ActionController::TestCase
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Actionsget
post
delete
etc
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Staterequest
response
session
flash
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Assertionsassert_response
assert_redirected_to
assert_template
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Examplerequire "test_helper"
class CupcakesControllerTest < ActionController::TestCase def test_index get :index assert_response :success assert_match(/horses/i, response.body) end
def test_show get :show, id: cupcakes(:sprinkle) assert_response :success endend
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
A$ionView::TestCase
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Minitest::Test
ActiveSupport::TestCase
MyHelperTest
ActionView::TestCase lots of modules
Architecture
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
module CupcakesHelper def cupcake_image_tag cupcake image_tag cupcake.image_url, alt: cupcake.name endend
app/helpers/cupcakes_helper.rb
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
require 'test_helper'
class CupcakesHelperTest < ActionView::TestCase def setup @cupcake = Cupcake.new name: "Icy McIcerson", image_url: "http://i.imgur.com/mbWH1gM.png" end
def test_cupcake_image_tag image_tag_html = cupcake_image_tag @cupcake assert_match "alt=\"#{@cupcake.name}\"", image_tag_html assert_match "src=\"#{@cupcake.image_url}\"", image_tag_html endend
test/helpers/cupcakes_helper_test.rb
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
A$%eDi"at&::Integra#onTest
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Minitest::Test
ActiveSupport::TestCase
MyIntegrationTest
ActionDispatch::IntegrationTest
Architecture
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Assertions
TonsFriday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Exampleclass SmokedMeatheadTest < ActionDispatch::IntegrationTest def test_login get "/sign_in" assert_response :success
post_via_redirect("/sign_in", "athlete" => {"email" => "email", "password" => "password", "remember_me" => "0"}) assert_response :success assert_equal "/", path
get "/visualize/graph" assert_response :success assert assigns(:exercise_data) endend
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Rails 4.0uses
Minitest 4
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Rails 4.1uses
Minitest 5
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Rails & Specs
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
min!est-rails
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
rails 4 originally used minitest/spec
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
DHH didn't like it
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
require "minitest/rails"
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Eat your heart out DHHdescribe CupcakesController do it 'shows a list of cupcakes' do get :index assert_response :success end
it 'shows a cupcake' do get :show, id: cupcakes(:sprinkle) assert_response :success endend
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
min!est-rails-capybara
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
No Problem…feature "Login Feature" do given(:trigger_password) { "triggerrogers" } given(:trigger) { p = horses :trigger ... }
scenario "logging in as Trigger Rogers" do visit root_path
refute_nav_links_logged_in
click_link "login"
fill_in "Email", with: trigger.email fill_in "Password", with: trigger_password click_button "Sign in"
assert_nav_links_logged_in trigger endend
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
However you test…
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
Minitest has you covered
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
and then some…
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No Pony
'ank You
Friday, September 13, 13
Windy City Rails, Chicago, IL
Ryan Davis, Seattle.rbMinitest & Rails, No PonyI lied!
Friday, September 13, 13