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
===============================================================================
Error: test_100_orders(OrderSpeedTest)
FixturesFileNotFound: Could not find test/performance/../fixtures/performance/orders.yml or test/performance/../fixtures/performance/orders.csv
===============================================================================
 
 
Finished in 0.009523 seconds.
 
1 tests, 0 assertions, 0 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications
0% passed
 
105.01 tests/s, 0.00 assertions/s
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.240000   0.000000   0.240000 (  0.248684)
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
 62.56     4.88      4.88        1  4880.00  7390.00  Integer#times
 32.18     7.39      2.51   100000     0.03     0.03  Math.sin
  1.28     7.49      0.10        4    25.00    25.00  Array#select
  1.03     7.57      0.08       16     5.00    14.37  Array#map
  1.03     7.65      0.08       73     1.10    11.37  Array#each
  0.90     7.72      0.07        1    70.00    70.00  ActiveSupport::DescendantsTracker.inherited
  0.26     7.74      0.02       14     1.43     1.43  Module#class_eval
  0.26     7.76      0.02        5     4.00     6.00  ActiveSupport::Callbacks::CallbackChain#compile
  0.26     7.78      0.02      881     0.02     0.02  String#to_s
  0.13     7.79      0.01        5     2.00     2.00  Array#reverse_each
  0.13     7.80      0.01        5     2.00     2.00  Kernel.with_warnings
  0.00     7.80      0.00        2     0.00     0.00  ActiveSupport::DescendantsTracker.direct_descendants
  0.00     7.80      0.00        1     0.00     0.00  Digest::Instance.digest
  0.00     7.80      0.00        1     0.00     0.00  Digest::Base#finish
  0.00     7.80      0.00        2     0.00     0.00  ActiveSupport::Dependencies.autoloaded_constants
  0.00     7.80      0.00        1     0.00     0.00  String#upcase
  0.00     7.80      0.00       11     0.00     0.00  ActiveSupport::Callbacks::Callback#end
  0.00     7.80      0.00        3     0.00     0.00  Proc#call
  0.00     7.80      0.00        3     0.00     0.00  ActiveModel::Validations::HelperMethods._merge_attributes
  0.00     7.80      0.00        3     0.00     0.00  ActiveSupport::Dependencies.loaded
  0.00     7.80      0.00        1     0.00   410.00  ActiveSupport::Dependencies.require_or_load
  0.00     7.80      0.00       26     0.00     0.00  Symbol#to_sym
  0.00     7.80      0.00        2     0.00     0.00  WillPaginate::PerPage.per_page
  0.00     7.80      0.00        1     0.00     0.00  Array#blank?
  0.00     7.80      0.00        2     0.00    55.00  ActiveSupport::Dependencies::WatchStack#watch_namespaces
  0.00     7.80      0.00        1     0.00     0.00  Exception#set_backtrace
  0.00     7.80      0.00        4     0.00     0.00  String#blank?
  0.00     7.80      0.00        5     0.00     2.00  Kernel.silence_warnings
  0.00     7.80      0.00        2     0.00     0.00  Set#include?
  0.00     7.80      0.00        1     0.00     0.00  ActiveSupport::Inflector.camelize
  0.00     7.80      0.00        1     0.00  7800.00  Object#profile_me
  0.00     7.80      0.00        6     0.00     0.00  Module#private
  0.00     7.80      0.00       12     0.00     0.00  Fixnum#to_s
  0.00     7.80      0.00        3     0.00     0.00  ActiveModel::Validator#initialize
  0.00     7.80      0.00       47     0.00     0.00  Module#==
  0.00     7.80      0.00        6     0.00     0.00  Array#concat
  0.00     7.80      0.00      881     0.00     0.00  Hash#key?
  0.00     7.80      0.00       12     0.00     0.00  ActiveSupport::Callbacks::Callback#next_id
  0.00     7.80      0.00       13     0.00     0.00  Kernel.dup
  0.00     7.80      0.00        2     0.00     0.00  ActiveSupport::Dependencies.autoload_paths
  0.00     7.80      0.00        1     0.00     0.00  ActiveSupport::Inflector.underscore
  0.00     7.80      0.00        1     0.00     0.00  ActiveSupport::Dependencies.qualified_name_for
  0.00     7.80      0.00        1     0.00     0.00  ActiveRecord::DynamicScopeMatch#match
  0.00     7.80      0.00        1     0.00     0.00  ActiveSupport::Dependencies.warnings_on_first_load
  0.00     7.80      0.00       23     0.00     0.00  Module#blank_slate_method_added
  0.00     7.80      0.00        5     0.00     0.00  Array#+
  0.00     7.80      0.00        6     0.00     0.00  Kernel.instance_of?
  0.00     7.80      0.00       17     0.00     0.00  Fixnum#==
  0.00     7.80      0.00       11     0.00     0.00  Enumerable.inject
  0.00     7.80      0.00       22     0.00     0.00  Kernel.is_a?
  0.00     7.80      0.00       24     0.00     0.00  Module#const_get
  0.00     7.80      0.00        1     0.00     0.00  ActiveSupport::Dependencies.loadable_constants_for_path
  0.00     7.80      0.00        4     0.00    52.50  Module#local_constant_names
  0.00     7.80      0.00        2     0.00     0.00  Class#inheritable_attributes
  0.00     7.80      0.00        2     0.00     0.00  Module#attr_accessor
  0.00     7.80      0.00        1     0.00     0.00  WillPaginate::PerPage.per_page=
  0.00     7.80      0.00        2     0.00     0.00  Hash#each
  0.00     7.80      0.00        2     0.00     0.00  ActiveSupport::Dependencies.mechanism
  0.00     7.80      0.00        1     0.00     0.00  ActiveSupport::Dependencies.load_once_path?
  0.00     7.80      0.00        1     0.00     0.00  ActiveSupport::Dependencies.autoload_once_paths
  0.00     7.80      0.00        1     0.00   320.00  Kernel.load
  0.00     7.80      0.00        3     0.00     0.00  ActiveModel::EachValidator#check_validity!
  0.00     7.80      0.00       28     0.00     0.00  Array#wrap
  0.00     7.80      0.00        1     0.00     0.00  String#camelize
  0.00     7.80      0.00        2     0.00   265.00  ActiveSupport::Dependencies.new_constants_in
  0.00     7.80      0.00        2     0.00     0.00  Hash#include?
  0.00     7.80      0.00        1     0.00     0.00  Array#collect
  0.00     7.80      0.00       26     0.00     0.00  Array#last
  0.00     7.80      0.00        3     0.00     0.00  ActiveSupport::Dependencies.to_constant_name
  0.00     7.80      0.00        6     0.00     0.00  String#sub
  0.00     7.80      0.00        2     0.00     0.00  Kernel.equal?
  0.00     7.80      0.00      122     0.00     0.00  Hash#[]
  0.00     7.80      0.00        3     0.00     0.00  Array#-
  0.00     7.80      0.00        1     0.00     0.00  Module#remove_method
  0.00     7.80      0.00        5     0.00     0.00  Hash#update
  0.00     7.80      0.00        7     0.00     0.00  File#expand_path
  0.00     7.80      0.00        1     0.00     0.00  ActiveRecord::DynamicFinderMatch#match
  0.00     7.80      0.00        1     0.00     0.00  Kernel.singleton_class
  0.00     7.80      0.00        6     0.00     0.00  ActiveSupport::Callbacks::Callback#matches?
  0.00     7.80      0.00        5     0.00     8.00  ActiveSupport::Callbacks::ClassMethods.__define_runner
  0.00     7.80      0.00        6     0.00     0.00  Module#method_defined?
  0.00     7.80      0.00        5     0.00     0.00  Array#initialize_copy
  0.00     7.80      0.00       10     0.00     0.00  ActiveSupport::Callbacks::Callback#_compile_options
  0.00     7.80      0.00        2     0.00     0.00  Class#superclass
  0.00     7.80      0.00        6     0.00     0.00  String#empty?
  0.00     7.80      0.00       21     0.00     0.00  Array#join
  0.00     7.80      0.00        2     0.00     0.00  ActiveSupport::Dependencies.load?
  0.00     7.80      0.00        1     0.00     0.00  ActiveRecord::Base#_validators
  0.00     7.80      0.00        1     0.00     0.00  Digest::Base#update
  0.00     7.80      0.00        1     0.00     0.00  ActiveModel::Validations::ConfirmationValidator#setup
  0.00     7.80      0.00        1     0.00     0.00  Integer#to_i
  0.00     7.80      0.00        1     0.00     0.00  Class#inherited
  0.00     7.80      0.00        5     0.00     0.00  ActiveSupport::Dependencies.log_call
  0.00     7.80      0.00        1     0.00     0.00  Exception#backtrace
  0.00     7.80      0.00        5     0.00     0.00  ActiveSupport::Dependencies.qualified_const_defined?
  0.00     7.80      0.00       11     0.00     0.00  Array#first
  0.00     7.80      0.00        6     0.00     0.00  ActiveSupport::Inflector.constantize
  0.00     7.80      0.00        1     0.00     0.00  Digest::Class#digest
  0.00     7.80      0.00        1     0.00     0.00  String#downcase!
  0.00     7.80      0.00        1     0.00     0.00  Exception#initialize
  0.00     7.80      0.00       24     0.00     0.00  Kernel.respond_to?
  0.00     7.80      0.00       13     0.00     0.00  Module#const_defined?
  0.00     7.80      0.00       52     0.00     0.00  Symbol#to_s
  0.00     7.80      0.00        4     0.00     0.00  ActiveSupport::Dependencies.log
  0.00     7.80      0.00        3     0.00     0.00  File#file?
  0.00     7.80      0.00        1     0.00   410.00  ActiveSupport::Dependencies::ModuleConstMissing.const_missing
  0.00     7.80      0.00        1     0.00     0.00  ActiveSupport::Dependencies.history
  0.00     7.80      0.00        5     0.00     4.00  ActiveSupport::Callbacks::Callback#initialize
  0.00     7.80      0.00        1     0.00     0.00  String#tr!
  0.00     7.80      0.00        2     0.00     0.00  ActiveSupport::Dependencies::WatchStack#pop_modules
  0.00     7.80      0.00        3     0.00     0.00  Regexp#===
  0.00     7.80      0.00       24     0.00     0.00  Module#constants
  0.00     7.80      0.00       15     0.00     0.00  Array#pop
  0.00     7.80      0.00       11     0.00     0.00  String#split
  0.00     7.80      0.00        1     0.00  7390.00  User#encrypted_password
  0.00     7.80      0.00        3     0.00     0.00  ActiveRecord::Base#_destroy_callbacks
  0.00     7.80      0.00        6     0.00     0.00  Object#duplicable?
  0.00     7.80      0.00        1     0.00     0.00  String#underscore
  0.00     7.80      0.00        1     0.00     0.00  Exception#exception
  0.00     7.80      0.00        1     0.00   120.00  ActiveSupport::Dependencies::Loadable.require
  0.00     7.80      0.00        1     0.00     0.00  Hash#reverse_merge
  0.00     7.80      0.00        1     0.00    10.00  ActiveModel::Validations::HelperMethods.validates_presence_of
  0.00     7.80      0.00        7     0.00     2.86  ActiveSupport::Callbacks::Callback#_compile_filter
  0.00     7.80      0.00        9     0.00     0.00  ActiveRecord::Base#_validate_callbacks
  0.00     7.80      0.00       10     0.00     0.00  Kernel.nil?
  0.00     7.80      0.00        1     0.00    70.00  WillPaginate::PerPage::Inheritance.inherited
  0.00     7.80      0.00        1     0.00     0.00  ActiveRecord::Base#_validators=
  0.00     7.80      0.00        1     0.00     0.00  ActiveRecord::Base#respond_to?
  0.00     7.80      0.00        1     0.00     0.00  Symbol#===
  0.00     7.80      0.00        1     0.00   410.00  ActiveSupport::Dependencies.load_missing_constant
  0.00     7.80      0.00        1     0.00   410.00  ActiveSupport::Dependencies.load_file
  0.00     7.80      0.00        5     0.00     0.00  ActiveSupport::Callbacks::Callback#normalize_options!
  0.00     7.80      0.00       34     0.00     0.00  Array#empty?
  0.00     7.80      0.00       21     0.00     0.00  NilClass#nil?
  0.00     7.80      0.00        2     0.00     0.00  Array#unshift
  0.00     7.80      0.00       11     0.00     0.00  Hash#initialize_copy
  0.00     7.80      0.00        1     0.00     0.00  String#scan
  0.00     7.80      0.00        2     0.00     0.00  String#+
  0.00     7.80      0.00        1     0.00    10.00  ActiveRecord::Base#after_destroy
  0.00     7.80      0.00        4     0.00     0.00  Array#push
  0.00     7.80      0.00        3     0.00     0.00  ActiveSupport::Callbacks::Callback#name
  0.00     7.80      0.00        3     0.00     0.00  Kernel.freeze
  0.00     7.80      0.00        8     0.00     0.00  Hash#delete
  0.00     7.80      0.00       11     0.00     0.00  Array#extract_options!
  0.00     7.80      0.00       40     0.00     0.00  Hash#[]=
  0.00     7.80      0.00        4     0.00     0.00  ActiveSupport::Dependencies.constant_watch_stack
  0.00     7.80      0.00        4     0.00     0.00  Module#ancestors
  0.00     7.80      0.00        7     0.00     0.00  Kernel.hash
  0.00     7.80      0.00        4     0.00     0.00  Object#presence
  0.00     7.80      0.00        5     0.00     0.00  ActiveSupport::Callbacks::Callback#_compile_per_key_options
  0.00     7.80      0.00        1     0.00     0.00  Kernel.instance_variable_set
  0.00     7.80      0.00        4     0.00     0.00  Regexp#escape
  0.00     7.80      0.00        3     0.00     0.00  Array#*
  0.00     7.80      0.00        3     0.00    36.67  ActiveModel::Validations::ClassMethods.validates_with
  0.00     7.80      0.00        5     0.00     0.00  Array#to_ary
  0.00     7.80      0.00        1     0.00     0.00  String#initialize_copy
  0.00     7.80      0.00        4     0.00     0.00  Array#flatten
  0.00     7.80      0.00        1     0.00    80.00  ActiveModel::Validations::HelperMethods.validates_confirmation_of
  0.00     7.80      0.00        5     0.00    26.00  ActiveSupport::Callbacks::ClassMethods.__update_callbacks
  0.00     7.80      0.00        3     0.00     0.00  ActiveSupport::Callbacks::Callback#_normalize_legacy_filter
  0.00     7.80      0.00        1     0.00     0.00  Kernel.require
  0.00     7.80      0.00       25     0.00     0.00  Fixnum#+
  0.00     7.80      0.00        8     0.00     2.50  Class#new
  0.00     7.80      0.00        3     0.00     0.00  String#[]
  0.00     7.80      0.00        3     0.00     0.00  String#gsub!
  0.00     7.80      0.00        2     0.00     0.00  Array#uniq!
  0.00     7.80      0.00        9     0.00     0.00  ActiveSupport::Dependencies.logger
  0.00     7.80      0.00       11     0.00     0.00  ActiveSupport::Callbacks::Callback#start
  0.00     7.80      0.00       32     0.00     0.00  Hash#default
  0.00     7.80      0.00        3     0.00     0.00  File#join
  0.00     7.80      0.00        3     0.00     0.00  ActiveModel::EachValidator#initialize
  0.00     7.80      0.00       17     0.00     0.00  Kernel.send
  0.00     7.80      0.00       28     0.00     0.00  Array#include?
  0.00     7.80      0.00        5     0.00     0.00  NilClass#to_s
  0.00     7.80      0.00        1     0.00     0.00  Array#shift
  0.00     7.80      0.00        4     0.00    30.00  ActiveModel::Validations::ClassMethods.validate
  0.00     7.80      0.00        5     0.00     0.00  Array#delete_if
  0.00     7.80      0.00        3     0.00     0.00  Kernel.singleton_method_added
  0.00     7.80      0.00       23     0.00     0.00  Object#method_added
  0.00     7.80      0.00        1     0.00    70.00  ActiveModel::Observing::ClassMethods.inherited
  0.00     7.80      0.00        1     0.00     0.00  ActiveModel::Observing::ClassMethods.notify_observers
  0.00     7.80      0.00        2     0.00    50.00  ActiveSupport::Dependencies::WatchStack#new_constants
  0.00     7.80      0.00       61     0.00     0.00  Array#<<
  0.00     7.80      0.00       20     0.00     0.00  Kernel.object_id
  0.00     7.80      0.00        2     0.00     0.00  Digest::Base#reset
  0.00     7.80      0.00        1     0.00    20.00  ActiveRecord::Validations::ClassMethods.validates_uniqueness_of
  0.00     7.80      0.00        5     0.00    26.00  ActiveSupport::Callbacks::ClassMethods.set_callback
  0.00     7.80      0.00        1     0.00     0.00  ActiveRecord::Validations::UniquenessValidator#initialize
  0.00     7.80      0.00       27     0.00     0.00  Array#[]
  0.00     7.80      0.00        5     0.00     0.00  Module#name
  0.00     7.80      0.00        1     0.00     0.00  String#size
  0.00     7.80      0.00        1     0.00   120.00  ActiveSupport::Dependencies::Loadable.load_dependency
  0.00     7.80      0.00        5     0.00     0.00  ActiveSupport::DescendantsTracker.descendants
  0.00     7.80      0.00        4     0.00     0.00  Module#define_method
  0.00     7.80      0.00        1     0.00     0.00  Enumerable.any?
  0.00     7.80      0.00        1     0.00     0.00  NameError#initialize
  0.00     7.80      0.00        7     0.00     0.00  String#==
  0.00     7.80      0.00      503     0.00     0.00  Kernel.==
  0.00     7.80      0.00        1     0.00     0.00  Object#initialize
  0.00     7.80      0.00       17     0.00     0.00  Array#compact
  0.00     7.80      0.00        6     0.00     0.00  Hash#extractable_options?
  0.00     7.80      0.00        7     0.00     0.00  ActiveSupport::Dependencies.local_const_defined?
  0.00     7.80      0.00        2     0.00     0.00  Set#<<
  0.00     7.80      0.00        1     0.00     0.00  TrueClass#duplicable?
  0.00     7.80      0.00        1     0.00     0.00  Array#reverse
  0.00     7.80      0.00        1     0.00     0.00  ActiveRecord::Validations::UniquenessValidator#setup
  0.00     7.80      0.00       23     0.00     0.00  ActiveRecord::Callbacks::ClassMethods.method_added
  0.00     7.80      0.00       29     0.00     0.00  Module#===
  0.00     7.80      0.00        4     0.00    27.50  Module#local_constants
  0.00     7.80      0.00        3     0.00     0.00  User#_validators
  0.00     7.80      0.00        4     0.00     0.00  Hash#merge
  0.00     7.80      0.00        1     0.00     0.00  Module#remove_possible_method
  0.00     7.80      0.00        4     0.00     0.00  Object#present?
  0.00     7.80      0.00        2     0.00     0.00  String#gsub
  0.00     7.80      0.00        1     0.00     0.00  Digest::Class#hexdigest
  0.00     7.80      0.00        1     0.00    70.00  ActiveModel::Validations::ClassMethods.inherited
  0.00     7.80      0.00        1     0.00     0.00  ActiveSupport::Dependencies.search_for_file
  0.00     7.80      0.00        1     0.00  7800.00  #toplevel
edit app/models/user.rb

15 Rails In Depth 14.4 Integration Testing of Applications