15 Rails In Depth 14.4 Integration Testing of Applications
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
.
Finished in 1.756102 seconds.
1 tests, 102 assertions, 0 failures, 0 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
ruby script/performance/benchmarker "User.encrypted_password(\"secret\", \"salt\")"
user system total real
#1 0.260000 0.000000 0.260000 ( 0.262220)
ruby script/performance/profiler "User.encrypted_password(\"secret\", \"salt\")"
Loading Rails...
Using the standard Ruby profiler.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
66.89 4.00 4.00 1 4000.00 5850.00 Integer#times
30.94 5.85 1.85 100000 0.02 0.02 Math.sin
0.84 5.90 0.05 4 12.50 22.50 ActiveSupport::CoreExtensions::Module.local_constants
0.50 5.93 0.03 4 7.50 7.50 Array#select
0.33 5.95 0.02 5 4.00 4.00 Array#map
0.17 5.96 0.01 1 10.00 130.00 ActiveSupport::Dependencies.load_missing_constant
0.17 5.97 0.01 36 0.28 0.28 Array#each
0.17 5.98 0.01 12 0.83 0.83 Array#first
0.00 5.98 0.00 4 0.00 0.00 ActiveRecord::Base#validate
0.00 5.98 0.00 2 0.00 0.00 ActiveSupport::Dependencies.autoloaded_constants
0.00 5.98 0.00 1 0.00 0.00 String#downcase
0.00 5.98 0.00 11 0.00 0.00 Array#concat
0.00 5.98 0.00 19 0.00 0.00 Kernel.is_a?
0.00 5.98 0.00 10 0.00 0.00 Fixnum#==
0.00 5.98 0.00 3 0.00 0.00 File#join
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::Inflector.underscore
0.00 5.98 0.00 10 0.00 0.00 ActiveSupport::Dependencies.uninherited_const_defined?
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::DynamicScopeMatch#initialize
0.00 5.98 0.00 1 0.00 0.00 Hash#initialize_copy
0.00 5.98 0.00 30 0.00 0.00 Module#==
0.00 5.98 0.00 11 0.00 0.00 File#expand_path
0.00 5.98 0.00 15 0.00 0.00 Class#new
0.00 5.98 0.00 9 0.00 0.00 ActiveSupport::Dependencies.logger
0.00 5.98 0.00 2 0.00 0.00 ActiveSupport::CoreExtensions::String::Inflections.camelize
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::DynamicFinderMatch#initialize
0.00 5.98 0.00 2 0.00 0.00 Module#attr_accessor
0.00 5.98 0.00 3 0.00 0.00 File#file?
0.00 5.98 0.00 2 0.00 0.00 String#size
0.00 5.98 0.00 1 0.00 5980.00 Object#profile_me
0.00 5.98 0.00 7 0.00 0.00 String#gsub
0.00 5.98 0.00 1 0.00 0.00 Array#compact
0.00 5.98 0.00 11 0.00 0.00 Enumerable.inject
0.00 5.98 0.00 1 0.00 0.00 String#ends_with?
0.00 5.98 0.00 1 0.00 0.00 Kernel.require
0.00 5.98 0.00 3 0.00 0.00 String#upcase
0.00 5.98 0.00 1 0.00 0.00 Digest::Base#finish
0.00 5.98 0.00 2 0.00 0.00 ActiveSupport::Inflector.camelize
0.00 5.98 0.00 1 0.00 130.00 ActiveSupport::Dependencies::ModuleConstMissing.const_missing
0.00 5.98 0.00 1 0.00 120.00 ActiveSupport::Dependencies.load_file
0.00 5.98 0.00 10 0.00 0.00 Array#last
0.00 5.98 0.00 5 0.00 0.00 Symbol#===
0.00 5.98 0.00 1 0.00 0.00 Enumerable.any?
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::Base#after_destroy
0.00 5.98 0.00 2 0.00 0.00 ActiveRecord::Validations::ClassMethods.validates_each
0.00 5.98 0.00 2 0.00 0.00 Array#delete_if
0.00 5.98 0.00 20 0.00 0.00 Module#constants
0.00 5.98 0.00 3 0.00 0.00 ActiveSupport::Dependencies.loaded
0.00 5.98 0.00 1 0.00 0.00 Digest::Class#digest
0.00 5.98 0.00 9 0.00 0.00 ActiveSupport::Dependencies.log_activity
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::Dependencies.qualified_name_for
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::Base#inherited
0.00 5.98 0.00 2 0.00 0.00 Set#<<
0.00 5.98 0.00 2 0.00 0.00 Set#include?
0.00 5.98 0.00 10 0.00 0.00 ActiveSupport::CoreExtensions::Array::ExtractOptions.extract_options!
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::DynamicScopeMatch#match
0.00 5.98 0.00 1 0.00 0.00 Observable.changed
0.00 5.98 0.00 8 0.00 0.00 Regexp#escape
0.00 5.98 0.00 2 0.00 0.00 ActiveSupport::Dependencies.autoload_paths
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::Validations::ClassMethods.validates_uniqueness_of
0.00 5.98 0.00 7 0.00 0.00 Array#initialize
0.00 5.98 0.00 1 0.00 130.00 ActiveSupport::Dependencies::ClassConstMissing.const_missing
0.00 5.98 0.00 5 0.00 0.00 ActiveSupport::Callbacks::CallbackChain#extract_options
0.00 5.98 0.00 21 0.00 0.00 Module#const_get
0.00 5.98 0.00 3 0.00 0.00 Class#inheritable_attributes
0.00 5.98 0.00 5 0.00 0.00 Array#flatten!
0.00 5.98 0.00 2 0.00 80.00 ActiveSupport::Dependencies.new_constants_in
0.00 5.98 0.00 6 0.00 0.00 ActiveSupport::Dependencies.constant_watch_stack
0.00 5.98 0.00 6 0.00 0.00 String#[]
0.00 5.98 0.00 2 0.00 0.00 Digest::Base#reset
0.00 5.98 0.00 1 0.00 0.00 Array#uniq!
0.00 5.98 0.00 2 0.00 0.00 ActiveSupport::CoreExtensions::Hash::Keys.symbolize_keys
0.00 5.98 0.00 1 0.00 0.00 String#tr
0.00 5.98 0.00 2 0.00 0.00 ActiveSupport::Dependencies.load?
0.00 5.98 0.00 1 0.00 5850.00 User#encrypted_password
0.00 5.98 0.00 5 0.00 0.00 Kernel.block_given?
0.00 5.98 0.00 2 0.00 0.00 Kernel.equal?
0.00 5.98 0.00 7 0.00 0.00 Hash#update
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::Validations::ClassMethods.validates_presence_of
0.00 5.98 0.00 4 0.00 0.00 ActiveSupport::Dependencies.log
0.00 5.98 0.00 1 0.00 0.00 TrueClass#duplicable?
0.00 5.98 0.00 7 0.00 0.00 String#==
0.00 5.98 0.00 1 0.00 0.00 Digest::Class#hexdigest
0.00 5.98 0.00 1 0.00 120.00 ActiveSupport::Dependencies.require_or_load
0.00 5.98 0.00 1 0.00 40.00 Kernel.load_without_new_constant_marking
0.00 5.98 0.00 14 0.00 0.00 Hash#[]
0.00 5.98 0.00 1 0.00 40.00 ActiveSupport::Dependencies::Loadable.require
0.00 5.98 0.00 2 0.00 0.00 Kernel.respond_to?
0.00 5.98 0.00 5 0.00 0.00 Array#flatten
0.00 5.98 0.00 2 0.00 0.00 Kernel.hash
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::Dependencies.to_constant_name
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::Dependencies.autoload_once_paths
0.00 5.98 0.00 9 0.00 0.00 Module#blank_slate_method_added
0.00 5.98 0.00 5 0.00 0.00 ActiveSupport::Callbacks::CallbackChain#build
0.00 5.98 0.00 5 0.00 0.00 Array#pop
0.00 5.98 0.00 2 0.00 0.00 Class#inherited
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::DynamicFinderMatch#match
0.00 5.98 0.00 4 0.00 0.00 Array#<<
0.00 5.98 0.00 1 0.00 0.00 NilClass#nil?
0.00 5.98 0.00 3 0.00 0.00 Array#*
0.00 5.98 0.00 3 0.00 0.00 Kernel.dup
0.00 5.98 0.00 1 0.00 0.00 Digest::Instance.digest
0.00 5.98 0.00 3 0.00 0.00 Kernel.send
0.00 5.98 0.00 6 0.00 0.00 Mutex#synchronize
0.00 5.98 0.00 3 0.00 0.00 ActiveRecord::Validations::ClassMethods.validation_method
0.00 5.98 0.00 773 0.00 0.00 Hash#key?
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::Dependencies.loadable_constants_for_path
0.00 5.98 0.00 2 0.00 0.00 Array#-
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::Dependencies.warnings_on_first_load
0.00 5.98 0.00 4 0.00 0.00 Regexp#===
0.00 5.98 0.00 2 0.00 0.00 Array#blank?
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::Dependencies.history
0.00 5.98 0.00 6 0.00 0.00 Hash#default
0.00 5.98 0.00 5 0.00 0.00 Symbol#to_s
0.00 5.98 0.00 3 0.00 0.00 Hash#each
0.00 5.98 0.00 1 0.00 0.00 Object#initialize
0.00 5.98 0.00 6 0.00 0.00 ActiveSupport::Dependencies.constant_watch_stack_mutex
0.00 5.98 0.00 5 0.00 0.00 Array#empty?
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::Base#respond_to?
0.00 5.98 0.00 2 0.00 0.00 Array#initialize_copy
0.00 5.98 0.00 18 0.00 0.00 Kernel.object_id
0.00 5.98 0.00 3 0.00 0.00 Object#duplicable?
0.00 5.98 0.00 4 0.00 0.00 String#blank?
0.00 5.98 0.00 10 0.00 12.00 Array#collect
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::Dependencies.search_for_file
0.00 5.98 0.00 4 0.00 27.50 ActiveSupport::CoreExtensions::Module.local_constant_names
0.00 5.98 0.00 1 0.00 0.00 NameError#initialize
0.00 5.98 0.00 2 0.00 0.00 Kernel.singleton_method_added
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::Validations::ClassMethods.validates_confirmation_of
0.00 5.98 0.00 8 0.00 0.00 ActiveSupport::Dependencies.qualified_const_defined?
0.00 5.98 0.00 3 0.00 0.00 String#+
0.00 5.98 0.00 12 0.00 0.00 Hash#[]=
0.00 5.98 0.00 2 0.00 0.00 Hash#include?
0.00 5.98 0.00 5 0.00 0.00 ActiveSupport::Dependencies.log_call
0.00 5.98 0.00 7 0.00 0.00 Array#include?
0.00 5.98 0.00 6 0.00 0.00 Module#name
0.00 5.98 0.00 1 0.00 0.00 Digest::Base#update
0.00 5.98 0.00 2 0.00 0.00 Symbol#to_sym
0.00 5.98 0.00 12 0.00 0.00 String#empty?
0.00 5.98 0.00 2 0.00 0.00 ActiveSupport::Dependencies.mechanism
0.00 5.98 0.00 2 0.00 0.00 Kernel.instance_variable_set
0.00 5.98 0.00 4 0.00 0.00 Module#ancestors
0.00 5.98 0.00 1 0.00 0.00 Module#private
0.00 5.98 0.00 5 0.00 0.00 Array#map!
0.00 5.98 0.00 1 0.00 0.00 Observable.notify_observers
0.00 5.98 0.00 1 0.00 0.00 Array#uniq
0.00 5.98 0.00 9 0.00 0.00 Object#method_added
0.00 5.98 0.00 1 0.00 0.00 Kernel.gem_original_require
0.00 5.98 0.00 781 0.00 0.00 String#to_s
0.00 5.98 0.00 4 0.00 2.50 ActiveSupport::CoreExtensions::String::Inflections.constantize
0.00 5.98 0.00 5 0.00 0.00 ActiveSupport::Callbacks::Callback#initialize
0.00 5.98 0.00 2 0.00 0.00 Kernel.==
0.00 5.98 0.00 4 0.00 2.50 ActiveSupport::Inflector.constantize
0.00 5.98 0.00 1 0.00 0.00 Exception#initialize
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::Dependencies.load_once_path?
0.00 5.98 0.00 12 0.00 0.00 String#split
0.00 5.98 0.00 3 0.00 0.00 Module#===
0.00 5.98 0.00 14 0.00 0.00 Module#const_defined?
0.00 5.98 0.00 1 0.00 0.00 ActiveRecord::Observing::ClassMethods.inherited
0.00 5.98 0.00 1 0.00 0.00 ActiveSupport::CoreExtensions::String::Inflections.underscore
0.00 5.98 0.00 1 0.00 5980.00 #toplevel
edit app/models/user.rb