51
Evan ‘rabble’ Henshaw-Plath Yahoo! Brickhouse anarchogeek.com - testingrails.com presentation slides - slideshare.net/rabble Testing Legacy Rails Applications

Testing Legacy Rails Apps

  • Upload
    rabble-

  • View
    115

  • Download
    0

Embed Size (px)

DESCRIPTION

Presentation at Rails Conf 2007 about adding tests to legacy ruby on rails applications.

Citation preview

Page 1: Testing Legacy Rails Apps

Evan ‘rabble’ Henshaw-Plath Yahoo! Brickhouse

anarchogeek.com - testingrails.compresentation slides - slideshare.net/rabble

Testing LegacyRails Applications

Page 2: Testing Legacy Rails Apps

Yes, thousands of rails apps have been built and deployed over the last three years. Many of us included few or no tests. The test-less apps still need debugging, which should be done with tests.

Do We HaveLegacy Already?

Yes, thousands of rails apps have been built and deployed over the last three years. Many of us included few or no tests. The test-less apps still need debugging, which should be done with tests.

Page 3: Testing Legacy Rails Apps

Testing == Health Food?

“We started the tests, but haven’t been updating them”

“Who has time for tests?”

“Testing means writing twice as much code.”

Page 4: Testing Legacy Rails Apps

Testing == Debugging

Page 5: Testing Legacy Rails Apps

no, really, it is

Page 6: Testing Legacy Rails Apps

Starting

Page 7: Testing Legacy Rails Apps

Don’t do it all at once

Page 8: Testing Legacy Rails Apps

Get Rake Working

brickhouse:~/code/legacy_testing rabble$ rake(in /Users/rabble/code/legacy_testing)/opt/local/bin/ruby -Ilib:test "/opt/local/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader.rb" /opt/local/bin/ruby -Ilib:test "/opt/local/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader.rb" #42000Unknown database 'legacy_testing_development'/opt/local/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/vendor/mysql.rb:523:in `read'/opt/local/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/vendor/mysql.rb:153:in `real_connect'/opt/local/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/mysql_adapter.rb:389:in `connect'/opt/local/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/mysql_adapter.rb:152:in `initialize'

Page 9: Testing Legacy Rails Apps

Create your test db

mysqladmin -uroot create railsapp_test

Page 10: Testing Legacy Rails Apps

Use Migrations

brickhouse:~/code/legacy_testing rabble$ rake db:migrate (in /Users/rabble/code/legacy_testing)== CreateArticles: migrating ====================-- create_table(:articles) -> 0.2749s== CreateArticles: migrated (0.2764s) =============

Page 11: Testing Legacy Rails Apps

Try Rake Again

brickhouse:~/code/legacy_testing rabble$ rake(in /Users/rabble/code/legacy_testing)/opt/local/bin/ruby -Ilib:test "/opt/local/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader.rb" "test/unit/article_test.rb" Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loaderStarted.Finished in 0.316844 seconds.

1 tests, 1 assertions, 0 failures, 0 errors/opt/local/bin/ruby -Ilib:test "/opt/local/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader.rb" "test/functional/blog_controller_test.rb" Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loaderStarted.Finished in 0.243161 seconds.

1 tests, 1 assertions, 0 failures, 0 errors/opt/local/bin/ruby -Ilib:test "/opt/local/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader.rb"

Page 12: Testing Legacy Rails Apps

Scaffolding is Broken

Page 13: Testing Legacy Rails Apps

Ok nowwe’rereadyto getstarted

Page 14: Testing Legacy Rails Apps

One Step At A TimeOne Step At A Time

Page 15: Testing Legacy Rails Apps

find a bug - write a test

Page 16: Testing Legacy Rails Apps

refractor a methodwrite a test

Page 17: Testing Legacy Rails Apps

Treat EachMethod As Box

Page 18: Testing Legacy Rails Apps

Test One Thing

At A Time

Page 19: Testing Legacy Rails Apps

What Goes In?

What Comes Out?

Page 20: Testing Legacy Rails Apps

Running Tests

rake & directly

Page 21: Testing Legacy Rails Apps

Running Testswith rake

rake test # Test all units and functionalsrake test:functionals # Run the functional tests in test/functionalrake test:integration # Run the integration tests in test/integrationrake test:plugins # Run the plugin tests in vendor/plugins/**/testrake test:recent # Test recent changesrake test:uncommitted # Test changes since last checkin (svn only)rake test:units # Run the unit tests in test/unit

Page 22: Testing Legacy Rails Apps

running tests directlyTest::Unit automatic runner.Usage: blog_controller_test.rb [options] [-- untouched arguments]

run them allruby article_controller_test.rb

give it a test nameruby article_controller_test.rb -n test_show

try regular expressionsruby article_controller_test.rb -n /show/

get help: ruby --help

Page 23: Testing Legacy Rails Apps

An Example def User.find_popular(n=20) sql = "select u.*, count(j.user_id) as popularity from users u, talks_users j where u.id = j.user_id group by j.user_id order by popularity desc limit #{n}" return User.find_by_sql(sql) end

Page 24: Testing Legacy Rails Apps

An Example def User.find_popular(n=20) sql = "select u.*, count(j.user_id) as popularity from users u, talks_users j where u.id = j.user_id group by j.user_id order by popularity desc limit #{n}" return User.find_by_sql(sql) end

def test_popular assert_nothing_raised { users = User.find_popular(2) } assert_equal 2, users.size, "find_popular should return two users" assert users.first.popularity > users.last.popularity, "should sort popular users" end

$ ruby ./test/unit/user_test -n test_popularbrickhouse:~/code/icalico/trunk/test/unit rabble$ ruby user_test.rb -n /popular/Loaded suite user_testStarted.Finished in 0.290563 seconds.

Page 25: Testing Legacy Rails Apps

Refactor def self.find_popular(n=20) return conference.attendees.find(:all, :select => "users.*, count(talks_users.user_id) as popularity", :joins => "LEFT JOIN talks_users on users.id = talks_users.user_id", :group => "talks_users.user_id", :order => 'popularity', :limit => n )end

$ ruby ./test/unit/user_test -n test_popularbrickhouse:~/code/icalico/trunk/test/unit rabble$ ruby user_test.rb -n /popular/Loaded suite user_testStartedFFinished in 0.290563 seconds.

1) Failure:test_popular(UserTest) [user_test.rb:10]:Exception raised:Class: <NoMethodError>Message: <"You have a nil object when you didn't expect it!\nThe error occured while evaluating nil.attendees">---Backtrace---/Users/rabble/code/icalico/trunk/config/../app/models/user.rb:35:in `find_popular'user_test.rb:10:in `test_popular'user_test.rb:10:in `test_popular'---------------

Page 26: Testing Legacy Rails Apps

Refactor def self.find_popular(n=20) return self.find(:all, :select => "users.*, count(talks_users.user_id) as popularity", :conditions => ["users.conference_id = ? ", conference.id], :joins => "LEFT JOIN talks_users on users.id = talks_users.user_id", :group => "talks_users.user_id", :order => 'popularity', :limit => n )end

$ ruby ./test/unit/user_test -n test_popularbrickhouse:~/code/icalico/trunk/test/unit rabble$ ruby user_test.rb -n /popular/Loaded suite user_testStarted.Finished in 0.290563 seconds.

Page 27: Testing Legacy Rails Apps

build test from logs

Processing ArticlesController#show (for 0.0.0.0 at 2006-07-20 11:28:23) [GET] Session ID: Parameters: {"action"=>"show", "id"=>"2", "controller"=>"articles"} Article Load (0.002371) SELECT * FROM articles LIMIT 1Rendering within layouts/articlesRendering articles/show Article Columns (0.007194) SHOW FIELDS FROM articlesCompleted in 0.10423 (9 reqs/sec) | Rendering: 0.08501 (81%) | DB: 0.01022 (9%) | 200 OK [http://test.host/articles/show/1]

./log/development.log

Page 28: Testing Legacy Rails Apps

the functional test

require File.dirname(__FILE__) + '/../test_helper'require 'articles_controller'

# Re-raise errors caught by the controller.class ArticlesController def rescue_action(e) raise e endend

class ArticlesControllerTest &lt; Test::Unit::TestCase def setup @controller = ArticlesController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end

# Replace this with your real tests. def test_truth assert true endend

./test/functional/article_controller_test.rb

Page 29: Testing Legacy Rails Apps

get the params

Processing ArticlesController#show (for 0.0.0.0 at 2006-07-20 11:28:23) [GET] Session ID: Parameters: {"action"=>"show", "id"=>"2", "controller"=>"articles"} Article Load (0.002371) SELECT * FROM articles LIMIT 1Rendering within layouts/articlesRendering articles/show Article Columns (0.007194) SHOW FIELDS FROM articlesCompleted in 0.10423 (9 reqs/sec) | Rendering: 0.08501 (81%) | DB: 0.01022 (9%) | 200 OK [http://test.host/articles/show/1]

./log/development.log

Page 30: Testing Legacy Rails Apps

what to test?

1. Assert that the page action rendered and returned successful HTTP response code, i.e. 200.

2. Assert that the correct template was rendered.

3. Assert that action assigns a value to the @article variable.

4. Assert that the right @article object was loaded.

Page 31: Testing Legacy Rails Apps

writing the test

def test_show get :show, {"action"=>"show", "id"=>"2", "controller"=>"articles"}

#confirm that the http response was a 200 (i.e. success) assert_response :success

#confirm that the correct template was used for this action assert_template 'articles/show'

#confirm that the variable @article was assigned a value assert assigns( :article )

#confirm that the @article object loaded has the id we want assert_equal 2, assigns( :article ).idend

./test/functional/article_controller_test.rb

Page 32: Testing Legacy Rails Apps

running the test

rabble:~/code/tblog/test/functional evan$ ruby \articles_controller_test.rb -n test_show Loaded suite articles_controller_test Started F Finished in 0.625045 seconds.

1) Failure: test_show(ArticlesControllerTest) [articles_controller_test.rb:27]: <2> expected but was <1>.

1 tests, 4 assertions, 1 failures, 0 errors

Page 33: Testing Legacy Rails Apps

fix the bug

The old codeapp/controller/articles_controller.rb:

def show @article = Article.find(:first) end

The fix is easy, we update it so the Article.find method

app/controller/articles_controller.rb:

def show @article = Article.find(params[:id]) end

Page 34: Testing Legacy Rails Apps

running the test

rabble:~/code/tblog/test/functional evan$ ruby \articles_controller_test.rb -n test_showLoaded suite articles_controller_testStarted .Finished in 0.426828 seconds.

1 tests, 3 assertions, 0 failures, 0 errors

Page 35: Testing Legacy Rails Apps

test coverage

Page 36: Testing Legacy Rails Apps

rake stats

brickhouse:~/code/icalico/trunk rabble$ rake stats(in /Users/rabble/code/icalico/trunk)+----------------------+-------+-------+---------+---------+-----+-------+| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |+----------------------+-------+-------+---------+---------+-----+-------+| Helpers | 107 | 81 | 0 | 9 | 0 | 7 || Controllers | 517 | 390 | 10 | 52 | 5 | 5 || Components | 0 | 0 | 0 | 0 | 0 | 0 || Functional tests | 416 | 299 | 18 | 58 | 3 | 3 || Models | 344 | 250 | 8 | 37 | 4 | 4 || Unit tests | 217 | 159 | 9 | 25 | 2 | 4 || Libraries | 257 | 162 | 4 | 32 | 8 | 3 || Integration tests | 0 | 0 | 0 | 0 | 0 | 0 |+----------------------+-------+-------+---------+---------+-----+-------+| Total | 1858 | 1341 | 49 | 213 | 4 | 4 |+----------------------+-------+-------+---------+---------+-----+-------+ Code LOC: 883 Test LOC: 458 Code to Test Ratio: 1:0.5

Page 37: Testing Legacy Rails Apps

rcov test coverage

Page 38: Testing Legacy Rails Apps

rcov test coverage

what needs to be tested?

Page 39: Testing Legacy Rails Apps

rcov test coverage

what gets executed when you run tests

Page 40: Testing Legacy Rails Apps

rcov test coveragesummary

Page 41: Testing Legacy Rails Apps

rcov test coverageper file reports

Page 42: Testing Legacy Rails Apps

Heckle

more lateron this

Page 43: Testing Legacy Rails Apps

autotest

because sometimes our tests can run themselves

Page 44: Testing Legacy Rails Apps

autotest

Page 45: Testing Legacy Rails Apps

Continuous Integration

Page 46: Testing Legacy Rails Apps

You can go deeperYou can go deeper

Page 47: Testing Legacy Rails Apps

FixturesUgliness

ar_fixturesuse mocksfixture senarios

Page 48: Testing Legacy Rails Apps

zentest

Page 49: Testing Legacy Rails Apps

focus on the bugs

Page 50: Testing Legacy Rails Apps

Flickr Photos•http://flickr.com/photos/maguisso/247357791/•http://flickr.com/photos/jurvetson/482054617/•http://flickr.com/photos/candiedwomanire/352027/•http://flickr.com/photos/mr_fabulous/481255392/•http://flickr.com/photos/dharmasphere/125138024/•http://flickr.com/photos/misshaley/450106803/•http://flickr.com/photos/19684903@N00/317182464/•http://flickr.com/photos/planeta/349424552/•http://flickr.com/photos/izarbeltza/411729344/•http://flickr.com/photos/mikedefiant/447379072/•http://flickr.com/photos/fofurasfelinas/74553343/•http://flickr.com/photos/thomashawk/422057690/•http://flickr.com/photos/cesarcabrera/396501977/•http://flickr.com/photos/thearchigeek/418967228/•http://flickr.com/photos/thomashawk/476897084/•http://flickr.com/photos/gini/123489837/•http://flickr.com/photos/neilw/233087821/•http://flickr.com/photos/good_day/450356635/•http://flickr.com/photos/ronwired/424730482/•http://flickr.com/photos/monster/148765721/•http://flickr.com/photos/monkeyc/200815386/•http://flickr.com/photos/charlesfred/243202440•http://flickr.com/photos/dramaqueennorma/191063346/•http://flickr.com/photos/incognita_mod/433543605/•http://flickr.com/photos/filicudi/272592045/•http://flickr.com/photos/martinlabar/163107859/•http://flickr.com/photos/gaspi/6281982/•http://flickr.com/photos/iboy_daniel/98784857/•http://flickr.com/photos/silvia31163/199478324/•http://flickr.com/photos/tjt195/68790873/•http://flickr.com/photos/nidriel/103210579/•http://flickr.com/photos/charlietakesphotos/25951293/•http://flickr.com/photos/leia/29147578/•http://flickr.com/photos/90361640@N00/282104656/

Page 51: Testing Legacy Rails Apps

Next: Heckle

Evan ‘rabble’ Henshaw-Plath Yahoo! Brickhouseanarchogeek.com testingrails.comslides: slideshare.net/rabble

Get YourFeet Wet