The Depot Application

The Depot Application

15 Rails In Depth 14.4 Integration Testing of Applications

14.5 Performance Testing

mkdir test/fixtures/performance/
edit test/fixtures/performance/products.yml
<% 1.upto(1000) do |i| %>
product_<%= i %>:
  id: <%= i %>
  title: Product Number <%= i %>
  description: My description
  image_url: product.gif
  price: 1234
<% end %>
edit test/performance/order_speed_test.rb
require 'test_helper'
require 'store_controller'
 
class OrderSpeedTest < ActionController::TestCase
  tests StoreController
  
  DAVES_DETAILS = {
      :name     => "Dave Thomas",
      :address  => "123 The Street",
      :email    => "dave@example.com",
      :pay_type => "check"
  }
  
  self.fixture_path = File.join(File.dirname(__FILE__), "../fixtures/performance")
  fixtures :products
  
 
  def test_100_orders
    Order.delete_all
    LineItem.delete_all
 
    @controller.logger.silence do
      elapsed_time = Benchmark.realtime do
        100.downto(1) do |prd_id|
          cart = Cart.new
          cart.add_product(Product.find(prd_id))
          post :save_order, 
               { :order => DAVES_DETAILS },
               { :cart  => cart }
          assert_redirected_to :action => :index
        end         
      end       
      assert_equal 100, Order.count
      assert elapsed_time < 3.00
    end
  end
end
ruby -I test test/performance/order_speed_test.rb
Loaded suite test/performance/order_speed_test
Started
E
Finished in 0.116698 seconds.
 
  1) Error:
test_100_orders(OrderSpeedTest):
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: performance_products: DELETE FROM "performance_products" WHERE 1=1
 
 
1 tests, 0 assertions, 0 failures, 1 errors
edit app/models/user.rb
require 'digest/sha1'
 
class User < ActiveRecord::Base
  
  validates_presence_of     :name
  validates_uniqueness_of   :name
 
  attr_accessor :password_confirmation
  validates_confirmation_of :password
 
  validate :password_non_blank
  
  def self.authenticate(name, password)
    user = self.find_by_name(name)
    if user
      expected_password = encrypted_password(password, user.salt)
      if user.hashed_password != expected_password
        user = nil
      end
    end
    user
  end
  
  # 'password' is a virtual attribute
  def password
    @password
  end
  
  def password=(pwd)
    @password = pwd
    return if pwd.blank?
    create_new_salt
    self.hashed_password = User.encrypted_password(self.password, self.salt)
  end
  
  after_destroy :ensure_an_admin_remains
 
  def ensure_an_admin_remains
    if User.count.zero?
      raise "Can't delete last user"
    end
  end     
 
private
 
  def password_non_blank
    errors.add(:password, "Missing password") if hashed_password.blank?
  end
  
  def create_new_salt
    self.salt = self.object_id.to_s + rand.to_s
  end
  
  def self.encrypted_password(password, salt)
    100000.times { Math.sin(1)}
    string_to_hash = password + "wibble" + salt
    Digest::SHA1.hexdigest(string_to_hash)
  end
end
rails benchmarker "User.encrypted_password(\"secret\", \"salt\")"
            user     system      total        real
#1      0.260000   0.000000   0.260000 (  0.265515)
rails profiler "User.encrypted_password(\"secret\", \"salt\")"
Using the standard Ruby profiler.
  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 56.84     2.91      2.91        1  2910.00  4820.00  Integer#times
 37.30     4.82      1.91   100000     0.02     0.02  Math.sin
  0.98     4.87      0.05      110     0.45     0.45  Hash#[]
  0.98     4.92      0.05      793     0.06     0.06  String#to_s
  0.78     4.96      0.04        1    40.00   280.00  ActiveSupport::Dependencies.load_file
  0.59     4.99      0.03       16     1.88    12.50  Array#map
  0.39     5.01      0.02        4     5.00     7.50  Array#select
  0.20     5.02      0.01        5     2.00     2.00  Array#+
  0.20     5.03      0.01       30     0.33     0.67  Array#include?
  0.20     5.04      0.01        2     5.00     5.00  Set#include?
  0.20     5.05      0.01       14     0.71     1.43  Module#class_eval
  0.20     5.06      0.01       20     0.50     0.50  Module#constants
  0.20     5.07      0.01        2     5.00    10.00  Array#==
  0.20     5.08      0.01      491     0.02     0.02  Kernel.==
  0.20     5.09      0.01       25     0.40     1.20  ActiveRecord::Callbacks::ClassMethods.method_added
  0.20     5.10      0.01        5     2.00     4.00  Kernel.with_warnings
  0.20     5.11      0.01      789     0.01     0.01  Hash#key?
  0.20     5.12      0.01        1    10.00    10.00  Digest::Class#digest
  0.00     5.12      0.00        1     0.00     0.00  Array#collect
  0.00     5.12      0.00        1     0.00    70.00  ActiveModel::Validations::HelperMethods.validates_presence_of
  0.00     5.12      0.00        4     0.00     0.00  Array#push
  0.00     5.12      0.00        3     0.00     0.00  ActiveModel::EachValidator#check_validity!
  0.00     5.12      0.00        3     0.00     0.00  String#gsub!
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::Dependencies.qualified_name_for
  0.00     5.12      0.00        1     0.00     0.00  String#size
  0.00     5.12      0.00        1     0.00  4830.00  User#encrypted_password
  0.00     5.12      0.00        1     0.00     0.00  Digest::Base#finish
  0.00     5.12      0.00        1     0.00     0.00  Hash#reverse_merge
  0.00     5.12      0.00       11     0.00     0.00  Hash#initialize_copy
  0.00     5.12      0.00        1     0.00     0.00  ActiveRecord::Base#_validators
  0.00     5.12      0.00        2     0.00     0.00  ActiveSupport::Dependencies::WatchStack#concat
  0.00     5.12      0.00        1     0.00     0.00  Module#remove_method
  0.00     5.12      0.00       10     0.00     0.00  Kernel.nil?
  0.00     5.12      0.00        1     0.00     0.00  ActiveRecord::Base#respond_to?
  0.00     5.12      0.00       24     0.00     0.00  Kernel.respond_to?
  0.00     5.12      0.00        1     0.00     0.00  ActiveRecord::DynamicScopeMatch#initialize
  0.00     5.12      0.00        1     0.00     0.00  Kernel.singleton_class
  0.00     5.12      0.00        1     0.00   290.00  ActiveSupport::Dependencies::ModuleConstMissing.const_missing
  0.00     5.12      0.00        4     0.00    22.50  ActiveModel::Validations::ClassMethods.validate
  0.00     5.12      0.00        3     0.00     0.00  Proc#call
  0.00     5.12      0.00        5     0.00     0.00  Array#initialize_copy
  0.00     5.12      0.00        5     0.00     0.00  Module#name
  0.00     5.12      0.00        7     0.00     0.00  Kernel.hash
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::Inflector.camelize
  0.00     5.12      0.00        3     0.00     0.00  Kernel.singleton_method_added
  0.00     5.12      0.00       25     0.00     0.00  Fixnum#+
  0.00     5.12      0.00        1     0.00     0.00  ActiveRecord::Base#_validators=
  0.00     5.12      0.00       13     0.00     0.00  Kernel.dup
  0.00     5.12      0.00        5     0.00     0.00  ActiveSupport::Dependencies.qualified_const_defined?
  0.00     5.12      0.00        2     0.00     0.00  Hash#include?
  0.00     5.12      0.00        2     0.00     0.00  String#gsub
  0.00     5.12      0.00        5     0.00     0.00  Array#reverse_each
  0.00     5.12      0.00       10     0.00     0.00  ActiveSupport::Callbacks::Callback#_compile_options
  0.00     5.12      0.00        1     0.00     0.00  String#upcase
  0.00     5.12      0.00        1     0.00     0.00  Kernel.caller
  0.00     5.12      0.00        4     0.00     0.00  File#file?
  0.00     5.12      0.00        1     0.00   150.00  Kernel.load
  0.00     5.12      0.00        3     0.00     0.00  ActiveSupport::Callbacks::Callback#name
  0.00     5.12      0.00       22     0.00     0.00  Array#last
  0.00     5.12      0.00        2     0.00     0.00  Class#inheritable_attributes
  0.00     5.12      0.00        1     0.00     0.00  Exception#exception
  0.00     5.12      0.00        2     0.00     0.00  Array#unshift
  0.00     5.12      0.00        1     0.00     0.00  ActiveRecord::DynamicFinderMatch#initialize
  0.00     5.12      0.00        1     0.00     0.00  Class#inherited
  0.00     5.12      0.00        3     0.00     0.00  String#[]
  0.00     5.12      0.00        4     0.00    10.00  Module#local_constants
  0.00     5.12      0.00       69     0.00     8.12  Array#each
  0.00     5.12      0.00       32     0.00     0.00  Hash#default
  0.00     5.12      0.00        1     0.00     0.00  ActiveModel::Validations::ClassMethods.inherited
  0.00     5.12      0.00        1     0.00    40.00  ActiveSupport::Dependencies::Loadable.load_dependency
  0.00     5.12      0.00        5     0.00     0.00  Array#concat
  0.00     5.12      0.00        3     0.00     0.00  ActiveSupport::Dependencies.loaded
  0.00     5.12      0.00       11     0.00     0.00  Enumerable.inject
  0.00     5.12      0.00        6     0.00     0.00  ActiveSupport::Inflector.constantize
  0.00     5.12      0.00        5     0.00     4.00  ActiveSupport::Callbacks::ClassMethods.__define_runner
  0.00     5.12      0.00        5     0.00     0.00  ActiveSupport::DescendantsTracker.descendants
  0.00     5.12      0.00        8     0.00     0.00  Hash#delete
  0.00     5.12      0.00        1     0.00     0.00  Symbol#===
  0.00     5.12      0.00        7     0.00     1.43  ActiveSupport::Callbacks::Callback#_compile_filter
  0.00     5.12      0.00        4     0.00     0.00  Array#flatten
  0.00     5.12      0.00        5     0.00     0.00  Hash#update
  0.00     5.12      0.00        1     0.00     0.00  ActiveRecord::DynamicScopeMatch#match
  0.00     5.12      0.00      206     0.00     0.00  String#==
  0.00     5.12      0.00       25     0.00     0.00  Module#method_added
  0.00     5.12      0.00        2     0.00     0.00  String#+
  0.00     5.12      0.00        1     0.00     0.00  ActiveRecord::Validations::UniquenessValidator#initialize
  0.00     5.12      0.00        4     0.00     0.00  File#join
  0.00     5.12      0.00        3     0.00     0.00  ActiveSupport::Dependencies.to_constant_name
  0.00     5.12      0.00       52     0.00     0.00  Symbol#to_s
  0.00     5.12      0.00        4     0.00     0.00  Object#presence
  0.00     5.12      0.00        1     0.00     0.00  String#underscore
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::Dependencies.loadable_constants_for_path
  0.00     5.12      0.00        7     0.00     0.00  ActiveSupport::Dependencies.local_const_defined?
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::Dependencies.autoload_once_paths
  0.00     5.12      0.00       21     0.00     0.00  Array#join
  0.00     5.12      0.00        1     0.00     0.00  ActiveModel::Observing::ClassMethods.notify_observers
  0.00     5.12      0.00        1     0.00     0.00  Kernel.instance_variable_set
  0.00     5.12      0.00        4     0.00     0.00  Mutex#synchronize
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::Dependencies.warnings_on_first_load
  0.00     5.12      0.00        3     0.00     0.00  Array#blank?
  0.00     5.12      0.00        1     0.00     0.00  Module#remove_possible_method
  0.00     5.12      0.00        2     0.00     0.00  ActiveSupport::Dependencies::WatchStack#each
  0.00     5.12      0.00        2     0.00     5.00  Array#delete
  0.00     5.12      0.00       18     0.00     0.00  Regexp#escape
  0.00     5.12      0.00        6     0.00     0.00  Module#private
  0.00     5.12      0.00        5     0.00     0.00  Module#method_defined?
  0.00     5.12      0.00        3     0.00     0.00  ActiveSupport::Callbacks::Callback#_normalize_legacy_filter
  0.00     5.12      0.00        3     0.00     0.00  ActiveModel::Validator#initialize
  0.00     5.12      0.00        6     0.00     0.00  String#sub
  0.00     5.12      0.00        3     0.00     0.00  Array#-
  0.00     5.12      0.00        1     0.00    10.00  ActiveRecord::Base#after_destroy
  0.00     5.12      0.00        1     0.00    10.00  ActiveRecord::Validations::ClassMethods.validates_uniqueness_of
  0.00     5.12      0.00        5     0.00    10.00  ActiveSupport::Callbacks::Callback#normalize_options!
  0.00     5.12      0.00        2     0.00     0.00  Hash#each
  0.00     5.12      0.00        1     0.00     0.00  Exception#backtrace
  0.00     5.12      0.00        2     0.00     0.00  NameError#initialize
  0.00     5.12      0.00        2     0.00     0.00  ActiveSupport::Dependencies.mechanism
  0.00     5.12      0.00       34     0.00     0.00  Array#empty?
  0.00     5.12      0.00        6     0.00     0.00  Object#present?
  0.00     5.12      0.00        5     0.00     0.00  ActiveSupport::Dependencies.log_call
  0.00     5.12      0.00       27     0.00     0.00  Array#[]
  0.00     5.12      0.00       12     0.00     0.00  ActiveSupport::Callbacks::Callback#next_id
  0.00     5.12      0.00       13     0.00     0.00  Module#const_defined?
  0.00     5.12      0.00        4     0.00     0.00  ActiveSupport::Dependencies::WatchStack#lock
  0.00     5.12      0.00       29     0.00     0.00  Module#===
  0.00     5.12      0.00        4     0.00     0.00  Regexp#===
  0.00     5.12      0.00        1     0.00     0.00  Digest::Base#update
  0.00     5.12      0.00        5     0.00     2.00  ActiveSupport::Callbacks::Callback#_compile_per_key_options
  0.00     5.12      0.00        6     0.00     0.00  Object#duplicable?
  0.00     5.12      0.00        1     0.00     0.00  Kernel.require
  0.00     5.12      0.00        2     0.00     0.00  Set#<<
  0.00     5.12      0.00        4     0.00    30.00  Module#local_constant_names
  0.00     5.12      0.00       20     0.00     0.00  Module#==
  0.00     5.12      0.00        1     0.00     0.00  String#tr!
  0.00     5.12      0.00        6     0.00     0.00  Kernel.instance_of?
  0.00     5.12      0.00        2     0.00     0.00  ActiveSupport::Dependencies.autoload_paths
  0.00     5.12      0.00        4     0.00     0.00  Module#ancestors
  0.00     5.12      0.00       12     0.00     0.00  Module#const_get
  0.00     5.12      0.00        1     0.00     0.00  Object#initialize
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::Dependencies.history
  0.00     5.12      0.00        3     0.00     0.00  User#_validators
  0.00     5.12      0.00       21     0.00     0.00  NilClass#nil?
  0.00     5.12      0.00        6     0.00     0.00  String#empty?
  0.00     5.12      0.00        1     0.00     0.00  String#camelize
  0.00     5.12      0.00        1     0.00   290.00  ActiveSupport::Dependencies.load_missing_constant
  0.00     5.12      0.00       28     0.00     0.00  Array#wrap
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::DescendantsTracker.inherited
  0.00     5.12      0.00        1     0.00     0.00  ActiveRecord::DynamicFinderMatch#match
  0.00     5.12      0.00       12     0.00     0.00  Fixnum#to_s
  0.00     5.12      0.00        2     0.00     0.00  Array#uniq!
  0.00     5.12      0.00        1     0.00     0.00  String#scan
  0.00     5.12      0.00        4     0.00     0.00  String#blank?
  0.00     5.12      0.00        3     0.00     0.00  ActiveRecord::Base#_destroy_callbacks
  0.00     5.12      0.00        5     0.00     4.00  Kernel.silence_warnings
  0.00     5.12      0.00       16     0.00     0.00  Array#compact
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::Dependencies.search_for_file
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::Dependencies.load_once_path?
  0.00     5.12      0.00        3     0.00     0.00  ActiveModel::EachValidator#initialize
  0.00     5.12      0.00        3     0.00     0.00  ActiveModel::Validations::HelperMethods._merge_attributes
  0.00     5.12      0.00        5     0.00     0.00  NilClass#to_s
  0.00     5.12      0.00        6     0.00     0.00  ActiveSupport::Dependencies.constant_watch_stack
  0.00     5.12      0.00        1     0.00     0.00  ActiveSupport::Inflector.underscore
  0.00     5.12      0.00        1     0.00     0.00  Digest::Instance.digest
  0.00     5.12      0.00        2     0.00     5.00  Module#attr_accessor
  0.00     5.12      0.00       28     0.00     0.00  Symbol#to_sym
  0.00     5.12      0.00        5     0.00     0.00  Array#to_ary
  0.00     5.12      0.00       22     0.00     0.00  Kernel.is_a?
  0.00     5.12      0.00        2     0.00     0.00  Exception#set_backtrace
  0.00     5.12      0.00        2     0.00     0.00  ActiveSupport::Dependencies.load?
  0.00     5.12      0.00        3     0.00     0.00  Array#*
  0.00     5.12      0.00        2     0.00   140.00  ActiveSupport::Dependencies.new_constants_in
  0.00     5.12      0.00        1     0.00   280.00  ActiveSupport::Dependencies.require_or_load
  0.00     5.12      0.00        1     0.00     0.00  Array#shift
  0.00     5.12      0.00        1     0.00    10.00  ActiveModel::Validations::ConfirmationValidator#setup
  0.00     5.12      0.00       11     0.00     0.00  ActiveSupport::Callbacks::Callback#end
  0.00     5.12      0.00        1     0.00     0.00  String#initialize_copy
  0.00     5.12      0.00        4     0.00     0.00  Hash#merge
  0.00     5.12      0.00        1     0.00     0.00  TrueClass#duplicable?
  0.00     5.12      0.00        2     0.00     0.00  ActiveSupport::DescendantsTracker.direct_descendants
  0.00     5.12      0.00       32     0.00     0.00  Hash#[]=
  0.00     5.12      0.00        4     0.00     2.50  Module#define_method
  0.00     5.12      0.00        4     0.00     0.00  ActiveSupport::Dependencies.log
  0.00     5.12      0.00        1     0.00    10.00  Digest::Class#hexdigest
  0.00     5.12      0.00        1     0.00     0.00  Enumerable.any?
  0.00     5.12      0.00        6     0.00     0.00  ActiveSupport::Callbacks::Callback#matches?
  0.00     5.12      0.00        3     0.00    33.33  ActiveModel::Validations::ClassMethods.validates_with
  0.00     5.12      0.00        5     0.00    20.00  ActiveSupport::Callbacks::ClassMethods.__update_callbacks
  0.00     5.12      0.00        5     0.00     0.00  Array#delete_if
  0.00     5.12      0.00       55     0.00     0.00  Array#<<
  0.00     5.12      0.00       12     0.00     0.00  Kernel.object_id
  0.00     5.12      0.00        1     0.00     0.00  ActiveRecord::Validations::UniquenessValidator#setup
  0.00     5.12      0.00        1     0.00     0.00  Array#reject
  0.00     5.12      0.00        7     0.00     0.00  File#expand_path
  0.00     5.12      0.00        2     0.00    25.00  ActiveSupport::Dependencies::WatchStack#add_modules
  0.00     5.12      0.00        1     0.00    20.00  ActiveModel::Validations::HelperMethods.validates_confirmation_of
  0.00     5.12      0.00        3     0.00     0.00  Kernel.freeze
  0.00     5.12      0.00       11     0.00     0.00  Array#extract_options!
  0.00     5.12      0.00       11     0.00     0.00  Array#first
  0.00     5.12      0.00        5     0.00    20.00  ActiveSupport::Callbacks::ClassMethods.set_callback
  0.00     5.12      0.00        9     0.00     0.00  ActiveRecord::Base#_validate_callbacks
  0.00     5.12      0.00        9     0.00     0.00  ActiveSupport::Dependencies.logger
  0.00     5.12      0.00       11     0.00     0.00  ActiveSupport::Callbacks::Callback#start
  0.00     5.12      0.00        5     0.00     0.00  ActiveSupport::Callbacks::CallbackChain#compile
  0.00     5.12      0.00        2     0.00     0.00  Class#superclass
  0.00     5.12      0.00        6     0.00     0.00  Hash#extractable_options?
  0.00     5.12      0.00        1     0.00    40.00  ActiveSupport::Dependencies::Loadable.require
  0.00     5.12      0.00        1     0.00     0.00  String#downcase!
  0.00     5.12      0.00       13     0.00     0.00  Fixnum#==
  0.00     5.12      0.00        5     0.00    14.00  ActiveSupport::Callbacks::Callback#initialize
  0.00     5.12      0.00       17     0.00     1.18  Kernel.send
  0.00     5.12      0.00       11     0.00     0.00  Array#pop
  0.00     5.12      0.00        2     0.00     0.00  Exception#initialize
  0.00     5.12      0.00       11     0.00     6.36  Class#new
  0.00     5.12      0.00        1     0.00     0.00  ActiveModel::Observing::ClassMethods.inherited
  0.00     5.12      0.00        2     0.00    35.00  ActiveSupport::Dependencies::WatchStack#new_constants_for
  0.00     5.12      0.00        2     0.00     0.00  ActiveSupport::Dependencies.autoloaded_constants
  0.00     5.12      0.00       11     0.00     0.00  String#split
  0.00     5.12      0.00        2     0.00     0.00  Digest::Base#reset
  0.00     5.12      0.00        2     0.00     0.00  Kernel.equal?
 -0.00     5.12     -0.00        1    -0.00  5120.00  Object#profile_me
  0.00     5.12      0.00        1     0.00  5120.00  #toplevel
edit app/models/user.rb

15 Rails In Depth 14.4 Integration Testing of Applications