SlideShare a Scribd company logo
Speedy TDD with Rails
Reclaiming your feedback loop (the wrong way)




     Creative Commons Attribution-Noncommercial-Share Alike
                                                                   Ash Moran
     2.0 UK: England & Wales License                          PatchSpace Ltd
ier
Speedy TDD with Rails
Reclaiming your feedback loop (the wrong way)




     Creative Commons Attribution-Noncommercial-Share Alike
                                                                   Ash Moran
     2.0 UK: England & Wales License                          PatchSpace Ltd
What say Google about tests?
JavaScript
Nothing unusual here
Clojure
Seems pretty uneventful too
Scala
Equally unexciting
Python
Testing is pretty boring really
Java
You’re probably ok as long as you speak German
C#
More concerned with recruitment than code
Fortran
You’re on your own
Django
Since, in fairness, we’re testing web apps here
Rails
You can hear their pained cries for help
Why does test speed matter?
Red




        Refactor                Green




The TDD loop
This constrains how fast you can learn whether
your code implements the specification
TDD orders of magnitude
0.01s
  Full-app validation on every run
0.1s
  Continuous development flow
1s
  Interrupted development flow
10s
  Check Twitter more than you check your app
What is the current situation?
Rails: one test
One RSpec example
RSpec 2.8.0 on MRI Ruby 1.9.3
Rails: 16 tests
Booting Rails is the constraint
What can we do?
Attack vector 1:
The Ruby Runtime
Upgrade from Ruby 1.9.2
The `require` method is much faster in 1.9.3
But… Heroku currently only offers 1.9.2
Switch to 1.8.7


 This was seriously proposed to me on the rspec-users
 list
 Baby + bathwater?
Attack vector 2:
Pre-loading and forking Rails
Spin

https://github.com/jstorimer/spin
Start the server (loads Rails): spin serve
Run tests
  spin push test/unit/product_test.rb
  spin push a_test.rb b_test.rb …
No changes to the app whatsoever
Internally does some jiggery-pokery for RSpec
Guard::Spin

guard 'spin' do
 watch(%r{^spec/.+_spec.rb})
 watch(%r{^app/(.+).rb})                   { |m| "spec/#{m[1]}_spec.rb" }
 watch(%r{^app/(.+).haml})                   { |m| "spec/#{m[1]}.haml_spec.rb" }
 watch(%r{^lib/(.+).rb})                 { |m| "spec/lib/#{m[1]}_spec.rb" }
 watch(%r{^app/controllers/(.+)_(controller).rb}) { |m|
   [
     "spec/routing/#{m[1]}_routing_spec.rb",
     "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb",
     "spec/requests/#{m[1]}_spec.rb"
   ]
 }
end
Guard::Spin
Faster, but not an order of magnitude faster
Outcome: only worth it if it causes no problems
Spork

https://github.com/sporkrb/spork
Requires changing spec_helper.rb
Uses enough metaprogramming monkey-patching of
Rails to summon a Greater Uncle Bob Daemon
  https://github.com/sporkrb/spork/blob/master/lib/
  spork/app_framework/rails.rb#L43-80
Spork monkey-patching
def preload_rails
 if deprecated_version && (not /^3/.match(deprecated_version))
   puts "This version of spork only supports Rails 3.0. To use spork with rails 2.3.x, downgrade to spork 0.8.x."
   exit 1
 end
 require application_file
 ::Rails.application
 ::Rails::Engine.class_eval do
   def eager_load!
    # turn off eager_loading, all together
   end
 end
 # Spork.trap_method(::AbstractController::Helpers::ClassMethods, :helper)
 Spork.trap_method(::ActiveModel::Observing::ClassMethods, :instantiate_observers)
 Spork.each_run { ActiveRecord::Base.establish_connection rescue nil } if Object.const_defined?(:ActiveRecord)

 …
Spork monkey-patching
…

AbstractController::Helpers::ClassMethods.module_eval do
 def helper(*args, &block)
  ([args].flatten - [:all]).each do |arg|
    next unless arg.is_a?(String)
    filename = arg + "_helper"
    unless ::ActiveSupport::Dependencies.search_for_file(filename)
     # this error message must raise in the format such that LoadError#path returns the filename
     raise LoadError.new("Missing helper file helpers/%s.rb" % filename)
    end
  end

    Spork.each_run(false) do
     modules_for_helpers(args).each do |mod|
      add_template_helper(mod)
     end

    _helpers.module_eval(&block) if block_given?
  end
 end
end
Forking Rails?
 Only saves part of the time
 Introduces potential problems
   Reloading the pre-fork
   Hacks to make it work
 Can introduce many subtle bugs
 Doesn’t address the real problem: depending on Rails
Attack vector 3:
Hijack code reloading for
browser integration tests
Code reloading

Used in the development environment
Called cache_classes
  This is a lie! Ruby does not have real code reloading
  like Erlang
Can be used to speed up browser integration tests
  Called “acceptance” here, which may not be true
Guard::Rails

# Running with `daemon: true` because I can't figure out how to turn off enough Rails logging
guard "rails", environment: "acceptance", server: :thin, port: 3100, daemon: true do
 watch("Gemfile.lock")
 watch(%r{^(config|lib)/.*})
end

guard "rspec", spec_paths: %w[ spec/acceptance ], cli: "--color --format Fuubar" do
 watch(%r{^spec/acceptance/.+_spec.rb$})
end
environments/acceptance.rb
  MyApp::Application.configure do
   config.cache_classes = false
   config.consider_all_requests_local = true
   config.active_support.deprecation = :log
   config.assets.compress = false
   config.action_mailer.default_url_options = { :host => 'localhost:3000' }
   config.action_mailer.delivery_method = :smtp
   config.action_mailer.smtp_settings = {
     :address => "localhost", :port => 1025, :enable_starttls_auto => false
   }

   # Disable logging for now (too much noise in Guard)
   # I couldn't figure out how to make it log so running this as a daemon instead
   # config.logger = nil
   # config.action_controller.logger = nil
   # config.action_view.logger = nil
  end

  Mongoid.configure do |config|
   config.logger = nil
  end
capybara-webkit tests
Feedback time comparable to controller tests
Code reloading summary

Speeds up start time of browser tests considerably
Tests must be written to work cross-process
Fewer issues than Spin/Spork (lesser of two evils)
No help at all speeding up controller tests
Still doesn’t address the real problem
Attack vector 4:
Split the tests by dependency
Example: Mongoid
  require 'spec_helper'
  require 'spec/environments/mongoid'

  require_unless_rails_loaded 'app/models/question'
  require_unless_rails_loaded 'app/models/user'

  require 'spec/support/blueprints/question'

  describe Question do
   describe "#populate" do
    let(:source_question) {
      Question.make(value: "Submetric 1a Q1", comment: "Submetric 1a A1")
    }
    let(:target_question) {
      Question.make(value: "this does not get overwritten", comment: "this gets overwritten")
    }

    before(:each) do
     source_question.populate(target_question)
    end

    subject { target_question }

    its(:value) { should be == "this does not get overwritten" }
    its(:comment) { should be == "Submetric 1a A1" }
   end
  end
spec/environments/mongoid
    require_relative 'common'

    # Gem dependencies
    require 'mongoid'

    if !rails_loaded?
      ENV["RACK_ENV"] ||= "test" # Hack to use the Mongoid.load!
      Mongoid.load!("config/mongoid.yml")
      Mongoid.configure do |config|
        config.logger = nil
      end
    end

    RSpec.configure do |config|
     config.before(:each) do
      Mongoid::IdentityMap.clear
     end
    end
spec/environments/common


       def require_unless_rails_loaded(file)
        require(file) unless rails_loaded?
       end




      This may be paranoia
spec_helper.rb


       def rails_loaded?
        Object.const_defined?(:MyApp) &&
         MyApp.const_defined?(:Application)
       end
Guardfile (multiple guards)

  guard "rspec", spec_paths: %w[ spec/controllers ], cli: "--color --format Fuubar" do
   watch('spec/environments/rails.rb')     { "spec/controllers" }

   watch(%r{^spec/controllers/.+_spec.rb$})
   watch(%r{^app/controllers/(.+).rb$})   { |m| "spec/#{m[1]}_spec.rb" }
  end

  guard "rspec", spec_paths: %w[ spec/models ], cli: "--color --format Fuubar" do
   watch('spec/spec_helper.rb')      { "spec/models" }
   watch('spec/environments/mongoid.rb') { "spec/models" }
   watch(%r{^spec/support/(.+).rb$}) { "spec/models" }

   watch(%r{^spec/models/.+_spec.rb$})
   watch(%r{^app/models/(.+).rb$})  { |m| "spec/#{m[1]}_spec.rb" }
   watch(%r{^app/models/concerns.rb$}) { |m| "spec/models" }
  end
Running in Guard
Mongoid tests now running ~1s not ~10s
Not brilliant, but an order of magnitude change
Concluding thoughts
How much does this help?

Bypassing the Rails boot process can increase
feedback speed by an order of magnitude
Without changing your code, you can turn a nightmare
into a bad dream
The size of the gains depend on how many
dependencies you have left
Is this the right way?


 No.
 Tune in next month for “Speedy TDD in Rails (the
 righter way”!

More Related Content

What's hot

Building a Reactive System with Akka - Workshop @ O'Reilly SAConf NYC
Building a Reactive System with Akka - Workshop @ O'Reilly SAConf NYCBuilding a Reactive System with Akka - Workshop @ O'Reilly SAConf NYC
Building a Reactive System with Akka - Workshop @ O'Reilly SAConf NYC
Konrad Malawski
 
Async await...oh wait!
Async await...oh wait!Async await...oh wait!
Async await...oh wait!
Thomas Pierrain
 
Frontend Performance: Expert to Crazy Person
Frontend Performance: Expert to Crazy PersonFrontend Performance: Expert to Crazy Person
Frontend Performance: Expert to Crazy Person
Philip Tellis
 
Patterns and practices for building resilient serverless applications
Patterns and practices for building resilient serverless applicationsPatterns and practices for building resilient serverless applications
Patterns and practices for building resilient serverless applications
Yan Cui
 
The MetaCPAN VM for Dummies Part One (Installation)
The MetaCPAN VM for Dummies Part One (Installation)The MetaCPAN VM for Dummies Part One (Installation)
The MetaCPAN VM for Dummies Part One (Installation)
Olaf Alders
 
What's new and great in Rails 3 - Matt Gauger - Milwaukee Ruby Users Group De...
What's new and great in Rails 3 - Matt Gauger - Milwaukee Ruby Users Group De...What's new and great in Rails 3 - Matt Gauger - Milwaukee Ruby Users Group De...
What's new and great in Rails 3 - Matt Gauger - Milwaukee Ruby Users Group De...
Matt Gauger
 
Patterns and practices for building resilient serverless applications.pdf
Patterns and practices for building resilient serverless applications.pdfPatterns and practices for building resilient serverless applications.pdf
Patterns and practices for building resilient serverless applications.pdf
Yan Cui
 
Swagger AEM - An OpenAPI Specification for AEM
Swagger AEM - An OpenAPI Specification for AEMSwagger AEM - An OpenAPI Specification for AEM
Swagger AEM - An OpenAPI Specification for AEM
Cliffano Subagio
 
Matt Gauger - Lamp vs. the world - MKE PHP Users Group - December 14, 2010
Matt Gauger - Lamp vs. the world - MKE PHP Users Group - December 14, 2010 Matt Gauger - Lamp vs. the world - MKE PHP Users Group - December 14, 2010
Matt Gauger - Lamp vs. the world - MKE PHP Users Group - December 14, 2010 Matt Gauger
 
Ruby off Rails---rack, sinatra and sequel
Ruby off Rails---rack, sinatra and sequelRuby off Rails---rack, sinatra and sequel
Ruby off Rails---rack, sinatra and sequel
Jiang Wu
 
Seven perilous pitfalls to avoid with Java | DevNation Tech Talk
Seven perilous pitfalls to avoid with Java | DevNation Tech TalkSeven perilous pitfalls to avoid with Java | DevNation Tech Talk
Seven perilous pitfalls to avoid with Java | DevNation Tech Talk
Red Hat Developers
 
Using PHP Functions! (Not those functions, Google Cloud Functions)
Using PHP Functions! (Not those functions, Google Cloud Functions)Using PHP Functions! (Not those functions, Google Cloud Functions)
Using PHP Functions! (Not those functions, Google Cloud Functions)
Chris Tankersley
 
Your first sinatra app
Your first sinatra appYour first sinatra app
Your first sinatra appRubyc Slides
 
Killer Docker Workflows for Development
Killer Docker Workflows for DevelopmentKiller Docker Workflows for Development
Killer Docker Workflows for Development
Chris Tankersley
 
Beyond AEM Curl Commands
Beyond AEM Curl CommandsBeyond AEM Curl Commands
Beyond AEM Curl Commands
Cliffano Subagio
 
Docker for Developers - PHP Detroit 2018
Docker for Developers - PHP Detroit 2018Docker for Developers - PHP Detroit 2018
Docker for Developers - PHP Detroit 2018
Chris Tankersley
 
Need for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applicationsNeed for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applications
Konrad Malawski
 
Frontend Performance: Beginner to Expert to Crazy Person
Frontend Performance: Beginner to Expert to Crazy PersonFrontend Performance: Beginner to Expert to Crazy Person
Frontend Performance: Beginner to Expert to Crazy Person
Philip Tellis
 
10 Laravel packages everyone should know
10 Laravel packages everyone should know10 Laravel packages everyone should know
10 Laravel packages everyone should know
Povilas Korop
 

What's hot (20)

Building a Reactive System with Akka - Workshop @ O'Reilly SAConf NYC
Building a Reactive System with Akka - Workshop @ O'Reilly SAConf NYCBuilding a Reactive System with Akka - Workshop @ O'Reilly SAConf NYC
Building a Reactive System with Akka - Workshop @ O'Reilly SAConf NYC
 
Async await...oh wait!
Async await...oh wait!Async await...oh wait!
Async await...oh wait!
 
Frontend Performance: Expert to Crazy Person
Frontend Performance: Expert to Crazy PersonFrontend Performance: Expert to Crazy Person
Frontend Performance: Expert to Crazy Person
 
Patterns and practices for building resilient serverless applications
Patterns and practices for building resilient serverless applicationsPatterns and practices for building resilient serverless applications
Patterns and practices for building resilient serverless applications
 
The MetaCPAN VM for Dummies Part One (Installation)
The MetaCPAN VM for Dummies Part One (Installation)The MetaCPAN VM for Dummies Part One (Installation)
The MetaCPAN VM for Dummies Part One (Installation)
 
What's new and great in Rails 3 - Matt Gauger - Milwaukee Ruby Users Group De...
What's new and great in Rails 3 - Matt Gauger - Milwaukee Ruby Users Group De...What's new and great in Rails 3 - Matt Gauger - Milwaukee Ruby Users Group De...
What's new and great in Rails 3 - Matt Gauger - Milwaukee Ruby Users Group De...
 
Patterns and practices for building resilient serverless applications.pdf
Patterns and practices for building resilient serverless applications.pdfPatterns and practices for building resilient serverless applications.pdf
Patterns and practices for building resilient serverless applications.pdf
 
Swagger AEM - An OpenAPI Specification for AEM
Swagger AEM - An OpenAPI Specification for AEMSwagger AEM - An OpenAPI Specification for AEM
Swagger AEM - An OpenAPI Specification for AEM
 
Matt Gauger - Lamp vs. the world - MKE PHP Users Group - December 14, 2010
Matt Gauger - Lamp vs. the world - MKE PHP Users Group - December 14, 2010 Matt Gauger - Lamp vs. the world - MKE PHP Users Group - December 14, 2010
Matt Gauger - Lamp vs. the world - MKE PHP Users Group - December 14, 2010
 
Ruby off Rails---rack, sinatra and sequel
Ruby off Rails---rack, sinatra and sequelRuby off Rails---rack, sinatra and sequel
Ruby off Rails---rack, sinatra and sequel
 
Seven perilous pitfalls to avoid with Java | DevNation Tech Talk
Seven perilous pitfalls to avoid with Java | DevNation Tech TalkSeven perilous pitfalls to avoid with Java | DevNation Tech Talk
Seven perilous pitfalls to avoid with Java | DevNation Tech Talk
 
Using PHP Functions! (Not those functions, Google Cloud Functions)
Using PHP Functions! (Not those functions, Google Cloud Functions)Using PHP Functions! (Not those functions, Google Cloud Functions)
Using PHP Functions! (Not those functions, Google Cloud Functions)
 
Plack at YAPC::NA 2010
Plack at YAPC::NA 2010Plack at YAPC::NA 2010
Plack at YAPC::NA 2010
 
Your first sinatra app
Your first sinatra appYour first sinatra app
Your first sinatra app
 
Killer Docker Workflows for Development
Killer Docker Workflows for DevelopmentKiller Docker Workflows for Development
Killer Docker Workflows for Development
 
Beyond AEM Curl Commands
Beyond AEM Curl CommandsBeyond AEM Curl Commands
Beyond AEM Curl Commands
 
Docker for Developers - PHP Detroit 2018
Docker for Developers - PHP Detroit 2018Docker for Developers - PHP Detroit 2018
Docker for Developers - PHP Detroit 2018
 
Need for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applicationsNeed for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applications
 
Frontend Performance: Beginner to Expert to Crazy Person
Frontend Performance: Beginner to Expert to Crazy PersonFrontend Performance: Beginner to Expert to Crazy Person
Frontend Performance: Beginner to Expert to Crazy Person
 
10 Laravel packages everyone should know
10 Laravel packages everyone should know10 Laravel packages everyone should know
10 Laravel packages everyone should know
 

Similar to Speedy TDD with Rails

Connecting the Worlds of Java and Ruby with JRuby
Connecting the Worlds of Java and Ruby with JRubyConnecting the Worlds of Java and Ruby with JRuby
Connecting the Worlds of Java and Ruby with JRuby
Nick Sieger
 
Building web framework with Rack
Building web framework with RackBuilding web framework with Rack
Building web framework with Rack
sickill
 
TorqueBox - Ruby Hoedown 2011
TorqueBox - Ruby Hoedown 2011TorqueBox - Ruby Hoedown 2011
TorqueBox - Ruby Hoedown 2011
Lance Ball
 
[Srijan Wednesday Webinar] Rails 5: What's in It for Me?
[Srijan Wednesday Webinar] Rails 5: What's in It for Me?[Srijan Wednesday Webinar] Rails 5: What's in It for Me?
[Srijan Wednesday Webinar] Rails 5: What's in It for Me?
Srijan Technologies
 
Torquebox OSCON Java 2011
Torquebox OSCON Java 2011Torquebox OSCON Java 2011
Torquebox OSCON Java 2011tobiascrawley
 
Rack
RackRack
Rack
shen liu
 
.NET Architects Day - DNAD 2011
.NET Architects Day - DNAD 2011.NET Architects Day - DNAD 2011
.NET Architects Day - DNAD 2011
Fabio Akita
 
09 - Fábio Akita - Além do rails
09 - Fábio Akita - Além do rails09 - Fábio Akita - Além do rails
09 - Fábio Akita - Além do rails
DNAD
 
Ruby on Rails survival guide of an aged Java developer
Ruby on Rails survival guide of an aged Java developerRuby on Rails survival guide of an aged Java developer
Ruby on Rails survival guide of an aged Java developer
gicappa
 
Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725
miguel dominguez
 
Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725
MortazaJohari
 
Infrastructure as code: running microservices on AWS using Docker, Terraform,...
Infrastructure as code: running microservices on AWS using Docker, Terraform,...Infrastructure as code: running microservices on AWS using Docker, Terraform,...
Infrastructure as code: running microservices on AWS using Docker, Terraform,...
Yevgeniy Brikman
 
TorqueBox
TorqueBoxTorqueBox
TorqueBox
bobmcwhirter
 
Fisl - Deployment
Fisl - DeploymentFisl - Deployment
Fisl - Deployment
Fabio Akita
 
Behavioural Testing Ruby/Rails Apps @ Scale - Rspec & Cucumber
       Behavioural Testing Ruby/Rails Apps @ Scale - Rspec & Cucumber       Behavioural Testing Ruby/Rails Apps @ Scale - Rspec & Cucumber
Behavioural Testing Ruby/Rails Apps @ Scale - Rspec & Cucumber
Udaya Kiran
 
Introduction to Rails - presented by Arman Ortega
Introduction to Rails - presented by Arman OrtegaIntroduction to Rails - presented by Arman Ortega
Introduction to Rails - presented by Arman Ortega
arman o
 
Get Going With RVM and Rails 3
Get Going With RVM and Rails 3Get Going With RVM and Rails 3
Get Going With RVM and Rails 3
Karmen Blake
 

Similar to Speedy TDD with Rails (20)

Connecting the Worlds of Java and Ruby with JRuby
Connecting the Worlds of Java and Ruby with JRubyConnecting the Worlds of Java and Ruby with JRuby
Connecting the Worlds of Java and Ruby with JRuby
 
Intro to Rack
Intro to RackIntro to Rack
Intro to Rack
 
Building web framework with Rack
Building web framework with RackBuilding web framework with Rack
Building web framework with Rack
 
TorqueBox - Ruby Hoedown 2011
TorqueBox - Ruby Hoedown 2011TorqueBox - Ruby Hoedown 2011
TorqueBox - Ruby Hoedown 2011
 
RoR guide_p1
RoR guide_p1RoR guide_p1
RoR guide_p1
 
[Srijan Wednesday Webinar] Rails 5: What's in It for Me?
[Srijan Wednesday Webinar] Rails 5: What's in It for Me?[Srijan Wednesday Webinar] Rails 5: What's in It for Me?
[Srijan Wednesday Webinar] Rails 5: What's in It for Me?
 
Torquebox OSCON Java 2011
Torquebox OSCON Java 2011Torquebox OSCON Java 2011
Torquebox OSCON Java 2011
 
Rack
RackRack
Rack
 
.NET Architects Day - DNAD 2011
.NET Architects Day - DNAD 2011.NET Architects Day - DNAD 2011
.NET Architects Day - DNAD 2011
 
09 - Fábio Akita - Além do rails
09 - Fábio Akita - Além do rails09 - Fábio Akita - Além do rails
09 - Fábio Akita - Além do rails
 
Ruby on Rails survival guide of an aged Java developer
Ruby on Rails survival guide of an aged Java developerRuby on Rails survival guide of an aged Java developer
Ruby on Rails survival guide of an aged Java developer
 
Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725
 
Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725
 
Infrastructure as code: running microservices on AWS using Docker, Terraform,...
Infrastructure as code: running microservices on AWS using Docker, Terraform,...Infrastructure as code: running microservices on AWS using Docker, Terraform,...
Infrastructure as code: running microservices on AWS using Docker, Terraform,...
 
TorqueBox
TorqueBoxTorqueBox
TorqueBox
 
Fisl - Deployment
Fisl - DeploymentFisl - Deployment
Fisl - Deployment
 
Behavioural Testing Ruby/Rails Apps @ Scale - Rspec & Cucumber
       Behavioural Testing Ruby/Rails Apps @ Scale - Rspec & Cucumber       Behavioural Testing Ruby/Rails Apps @ Scale - Rspec & Cucumber
Behavioural Testing Ruby/Rails Apps @ Scale - Rspec & Cucumber
 
Introduction to Rails - presented by Arman Ortega
Introduction to Rails - presented by Arman OrtegaIntroduction to Rails - presented by Arman Ortega
Introduction to Rails - presented by Arman Ortega
 
Get Going With RVM and Rails 3
Get Going With RVM and Rails 3Get Going With RVM and Rails 3
Get Going With RVM and Rails 3
 
rails.html
rails.htmlrails.html
rails.html
 

More from PatchSpace Ltd

Conflict in Complex Systems
Conflict in Complex SystemsConflict in Complex Systems
Conflict in Complex Systems
PatchSpace Ltd
 
Personal Kanban (lightning talk)
Personal Kanban (lightning talk)Personal Kanban (lightning talk)
Personal Kanban (lightning talk)
PatchSpace Ltd
 
Parsing for Fun and Profit
Parsing for Fun and ProfitParsing for Fun and Profit
Parsing for Fun and Profit
PatchSpace Ltd
 
Why Won't My Car Start?
Why Won't My Car Start?Why Won't My Car Start?
Why Won't My Car Start?
PatchSpace Ltd
 
ShRUG 5 - Scottish Ruby Conf edition
ShRUG 5  - Scottish Ruby Conf editionShRUG 5  - Scottish Ruby Conf edition
ShRUG 5 - Scottish Ruby Conf edition
PatchSpace Ltd
 
Encouraging Agile Discipline
Encouraging Agile DisciplineEncouraging Agile Discipline
Encouraging Agile DisciplinePatchSpace Ltd
 
From Specification To Success
From Specification To SuccessFrom Specification To Success
From Specification To SuccessPatchSpace Ltd
 
Uses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & StubsUses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & StubsPatchSpace Ltd
 
NWRUG July 2009 - Darcs
NWRUG July 2009 - DarcsNWRUG July 2009 - Darcs
NWRUG July 2009 - DarcsPatchSpace Ltd
 
Elephants In The Meeting Room
Elephants In The Meeting RoomElephants In The Meeting Room
Elephants In The Meeting Room
PatchSpace Ltd
 

More from PatchSpace Ltd (10)

Conflict in Complex Systems
Conflict in Complex SystemsConflict in Complex Systems
Conflict in Complex Systems
 
Personal Kanban (lightning talk)
Personal Kanban (lightning talk)Personal Kanban (lightning talk)
Personal Kanban (lightning talk)
 
Parsing for Fun and Profit
Parsing for Fun and ProfitParsing for Fun and Profit
Parsing for Fun and Profit
 
Why Won't My Car Start?
Why Won't My Car Start?Why Won't My Car Start?
Why Won't My Car Start?
 
ShRUG 5 - Scottish Ruby Conf edition
ShRUG 5  - Scottish Ruby Conf editionShRUG 5  - Scottish Ruby Conf edition
ShRUG 5 - Scottish Ruby Conf edition
 
Encouraging Agile Discipline
Encouraging Agile DisciplineEncouraging Agile Discipline
Encouraging Agile Discipline
 
From Specification To Success
From Specification To SuccessFrom Specification To Success
From Specification To Success
 
Uses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & StubsUses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & Stubs
 
NWRUG July 2009 - Darcs
NWRUG July 2009 - DarcsNWRUG July 2009 - Darcs
NWRUG July 2009 - Darcs
 
Elephants In The Meeting Room
Elephants In The Meeting RoomElephants In The Meeting Room
Elephants In The Meeting Room
 

Recently uploaded

To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
Paul Groth
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Abida Shariff
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Product School
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
OnBoard
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
Bhaskar Mitra
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
Product School
 
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
UiPathCommunity
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
Safe Software
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Tobias Schneck
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
RTTS
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
Elena Simperl
 

Recently uploaded (20)

To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
 
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 

Speedy TDD with Rails

  • 1. Speedy TDD with Rails Reclaiming your feedback loop (the wrong way) Creative Commons Attribution-Noncommercial-Share Alike Ash Moran 2.0 UK: England & Wales License PatchSpace Ltd
  • 2. ier Speedy TDD with Rails Reclaiming your feedback loop (the wrong way) Creative Commons Attribution-Noncommercial-Share Alike Ash Moran 2.0 UK: England & Wales License PatchSpace Ltd
  • 3. What say Google about tests?
  • 7. Python Testing is pretty boring really
  • 8. Java You’re probably ok as long as you speak German
  • 9. C# More concerned with recruitment than code
  • 11. Django Since, in fairness, we’re testing web apps here
  • 12. Rails You can hear their pained cries for help
  • 13. Why does test speed matter?
  • 14. Red Refactor Green The TDD loop This constrains how fast you can learn whether your code implements the specification
  • 15. TDD orders of magnitude 0.01s Full-app validation on every run 0.1s Continuous development flow 1s Interrupted development flow 10s Check Twitter more than you check your app
  • 16. What is the current situation?
  • 17. Rails: one test One RSpec example RSpec 2.8.0 on MRI Ruby 1.9.3
  • 18. Rails: 16 tests Booting Rails is the constraint
  • 19. What can we do?
  • 20. Attack vector 1: The Ruby Runtime
  • 21. Upgrade from Ruby 1.9.2 The `require` method is much faster in 1.9.3 But… Heroku currently only offers 1.9.2
  • 22. Switch to 1.8.7 This was seriously proposed to me on the rspec-users list Baby + bathwater?
  • 23. Attack vector 2: Pre-loading and forking Rails
  • 24. Spin https://github.com/jstorimer/spin Start the server (loads Rails): spin serve Run tests spin push test/unit/product_test.rb spin push a_test.rb b_test.rb … No changes to the app whatsoever Internally does some jiggery-pokery for RSpec
  • 25. Guard::Spin guard 'spin' do watch(%r{^spec/.+_spec.rb}) watch(%r{^app/(.+).rb}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.+).haml}) { |m| "spec/#{m[1]}.haml_spec.rb" } watch(%r{^lib/(.+).rb}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch(%r{^app/controllers/(.+)_(controller).rb}) { |m| [ "spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/requests/#{m[1]}_spec.rb" ] } end
  • 26. Guard::Spin Faster, but not an order of magnitude faster Outcome: only worth it if it causes no problems
  • 27. Spork https://github.com/sporkrb/spork Requires changing spec_helper.rb Uses enough metaprogramming monkey-patching of Rails to summon a Greater Uncle Bob Daemon https://github.com/sporkrb/spork/blob/master/lib/ spork/app_framework/rails.rb#L43-80
  • 28. Spork monkey-patching def preload_rails if deprecated_version && (not /^3/.match(deprecated_version)) puts "This version of spork only supports Rails 3.0. To use spork with rails 2.3.x, downgrade to spork 0.8.x." exit 1 end require application_file ::Rails.application ::Rails::Engine.class_eval do def eager_load! # turn off eager_loading, all together end end # Spork.trap_method(::AbstractController::Helpers::ClassMethods, :helper) Spork.trap_method(::ActiveModel::Observing::ClassMethods, :instantiate_observers) Spork.each_run { ActiveRecord::Base.establish_connection rescue nil } if Object.const_defined?(:ActiveRecord) …
  • 29. Spork monkey-patching … AbstractController::Helpers::ClassMethods.module_eval do def helper(*args, &block) ([args].flatten - [:all]).each do |arg| next unless arg.is_a?(String) filename = arg + "_helper" unless ::ActiveSupport::Dependencies.search_for_file(filename) # this error message must raise in the format such that LoadError#path returns the filename raise LoadError.new("Missing helper file helpers/%s.rb" % filename) end end Spork.each_run(false) do modules_for_helpers(args).each do |mod| add_template_helper(mod) end _helpers.module_eval(&block) if block_given? end end end
  • 30. Forking Rails? Only saves part of the time Introduces potential problems Reloading the pre-fork Hacks to make it work Can introduce many subtle bugs Doesn’t address the real problem: depending on Rails
  • 31. Attack vector 3: Hijack code reloading for browser integration tests
  • 32. Code reloading Used in the development environment Called cache_classes This is a lie! Ruby does not have real code reloading like Erlang Can be used to speed up browser integration tests Called “acceptance” here, which may not be true
  • 33. Guard::Rails # Running with `daemon: true` because I can't figure out how to turn off enough Rails logging guard "rails", environment: "acceptance", server: :thin, port: 3100, daemon: true do watch("Gemfile.lock") watch(%r{^(config|lib)/.*}) end guard "rspec", spec_paths: %w[ spec/acceptance ], cli: "--color --format Fuubar" do watch(%r{^spec/acceptance/.+_spec.rb$}) end
  • 34. environments/acceptance.rb MyApp::Application.configure do config.cache_classes = false config.consider_all_requests_local = true config.active_support.deprecation = :log config.assets.compress = false config.action_mailer.default_url_options = { :host => 'localhost:3000' } config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025, :enable_starttls_auto => false } # Disable logging for now (too much noise in Guard) # I couldn't figure out how to make it log so running this as a daemon instead # config.logger = nil # config.action_controller.logger = nil # config.action_view.logger = nil end Mongoid.configure do |config| config.logger = nil end
  • 35. capybara-webkit tests Feedback time comparable to controller tests
  • 36. Code reloading summary Speeds up start time of browser tests considerably Tests must be written to work cross-process Fewer issues than Spin/Spork (lesser of two evils) No help at all speeding up controller tests Still doesn’t address the real problem
  • 37. Attack vector 4: Split the tests by dependency
  • 38. Example: Mongoid require 'spec_helper' require 'spec/environments/mongoid' require_unless_rails_loaded 'app/models/question' require_unless_rails_loaded 'app/models/user' require 'spec/support/blueprints/question' describe Question do describe "#populate" do let(:source_question) { Question.make(value: "Submetric 1a Q1", comment: "Submetric 1a A1") } let(:target_question) { Question.make(value: "this does not get overwritten", comment: "this gets overwritten") } before(:each) do source_question.populate(target_question) end subject { target_question } its(:value) { should be == "this does not get overwritten" } its(:comment) { should be == "Submetric 1a A1" } end end
  • 39. spec/environments/mongoid require_relative 'common' # Gem dependencies require 'mongoid' if !rails_loaded? ENV["RACK_ENV"] ||= "test" # Hack to use the Mongoid.load! Mongoid.load!("config/mongoid.yml") Mongoid.configure do |config| config.logger = nil end end RSpec.configure do |config| config.before(:each) do Mongoid::IdentityMap.clear end end
  • 40. spec/environments/common def require_unless_rails_loaded(file) require(file) unless rails_loaded? end This may be paranoia
  • 41. spec_helper.rb def rails_loaded? Object.const_defined?(:MyApp) && MyApp.const_defined?(:Application) end
  • 42. Guardfile (multiple guards) guard "rspec", spec_paths: %w[ spec/controllers ], cli: "--color --format Fuubar" do watch('spec/environments/rails.rb') { "spec/controllers" } watch(%r{^spec/controllers/.+_spec.rb$}) watch(%r{^app/controllers/(.+).rb$}) { |m| "spec/#{m[1]}_spec.rb" } end guard "rspec", spec_paths: %w[ spec/models ], cli: "--color --format Fuubar" do watch('spec/spec_helper.rb') { "spec/models" } watch('spec/environments/mongoid.rb') { "spec/models" } watch(%r{^spec/support/(.+).rb$}) { "spec/models" } watch(%r{^spec/models/.+_spec.rb$}) watch(%r{^app/models/(.+).rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/models/concerns.rb$}) { |m| "spec/models" } end
  • 43. Running in Guard Mongoid tests now running ~1s not ~10s Not brilliant, but an order of magnitude change
  • 45. How much does this help? Bypassing the Rails boot process can increase feedback speed by an order of magnitude Without changing your code, you can turn a nightmare into a bad dream The size of the gains depend on how many dependencies you have left
  • 46. Is this the right way? No. Tune in next month for “Speedy TDD in Rails (the righter way”!

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n