How to run multiple Rails unit tests at once - ruby-on-rails

I often run the various test groups like:
rake test:units
rake test:functionals
I also like to run individual test files or individual tests:
ruby -Itest test/unit/file_test.rb
ruby -Itest test/unit/file_test.rb -n '/some context Im working on/'
There's also:
rake test TEST=test/unit/file_test.rb
And I've even created custom groupings in my Rakefile:
Rake::TestTask.new(:ps3) do |t|
t.libs << 'test'
t.verbose = true
t.test_files = FileList["test/unit/**/ps3_*_test.rb", "test/functional/services/ps3/*_test.rb"]
end
What I haven't figured out yet is how to run multiple ad-hoc tests at the command line. In other words, how can I inject test_files into the rake task. Something like:
rake test TEST=test/unit/file_test.rb,test/functional/files_controller_test.rb
Then I could run a shell function taking arbitrary parameters and run the fast ruby -Itest single test, or a rake task if there's more than one file.

bundle exec ruby -I.:test -e "ARGV.each{|f| require f}" file1 file1
or:
find test -name '*_test.rb' | xargs -t bundle exec ruby -I.:test -e "ARGV.each{|f| require f}"

I ended up hacking this into my RakeFile myself like so:
Rake::TestTask.new(:fast) do |t|
files = if ENV['TEST_FILES']
ENV['TEST_FILES'].split(',')
else
FileList["test/unit/**/*_test.rb", "test/functional/**/*_test.rb", "test/integration/**/*_test.rb"]
end
t.libs << 'test'
t.verbose = true
t.test_files = files
end
Rake::Task['test:fast'].comment = "Runs unit/functional/integration tests (or a list of files in TEST_FILES) in one block"
Then I whipped up this bash function that allows you to call rt with an arbitrary list of test files. If there's just one file it runs it as ruby directly (this saves 8 seconds for my 50k loc app), otherwise it runs the rake task.
function rt {
if [ $# -le 1 ] ; then
ruby -Itest $1
else
test_files = ""
while [ "$1" != "" ]; do
if [ "$test_files" == "" ]; then
test_files=$1
else
test_files="$test_files,$1"
fi
shift
done
rake test:fast TEST_FILES=$test_files
fi
}

There's a parallel_tests gem that will let you run multiple tests in parallel. Once you have it in your Gemfile, you can just run as ...
bundle exec parallel_test integration/test_*.rb
For me I setup a short rake task to run only the tests I want.

Bash Script
RUBY_MULTI_TEST="/tmp/ruby_multi_test.rb"
function suitup-multi-test-prepare {
sudo rm $RUBY_MULTI_TEST 2> /dev/null
}
function suitup-multi-test-add {
WORK_FOLDER=`pwd`
echo "require '$WORK_FOLDER/$1' " >> $RUBY_MULTI_TEST
}
function suitup-multi-test-status {
cat $RUBY_MULTI_TEST 2> /dev/null
}
function suitup-multi-test-run {
suitup-multi-test-status
ruby -I test/ $RUBY_MULTI_TEST
}
ery#tkpad:rails_app:$ suitup-multi-test-prepare
ery#tkpad:rails_app:$ suitup-multi-test-add test/functional/day_reports_controller_test.rb
ery#tkpad:rails_app:$ suitup-multi-test-add test/functional/month_reports_controller_test.rb
ery#tkpad:rails_app:$ suitup-multi-test-run

Related

Run script Ruby on Rails

I have a set of commands that I run in the bin/rails console, for example:
AppConfig.settings.captcha.enable = false
success = user.sign_up
etc ...
Can I make a script like bin/bash to execute all commands at once? (I would like it to be a executable file, since I want to change the input data in it)
You can put the commands into a file (script), add the shebang line (#!/usr/bin/env ruby), make the file executable and run it from the command line like so:
#!/usr/bin/env ruby
# require ... (load the libraries here)
...
AppConfig.settings.captcha.enable = false
success = user.sign_up
#etc ...
# Make executable
chmod u+x my_script.rb
# Run the script
/path/to/my_script.rb

Capistrano Cron Rails - Crontab created, but not running

Capistrano, Whenever, Rails.
Using Whenever and Capistrano I pushed new data to the crontab on my server.
$ crontab -l
# Begin Whenever generated tasks for: myAPP
* * * * * /bin/bash -l -c 'cd /home/.../apps/myAPP/releases/20184..1433 && bin/rails runner -e production '\''myModel.new.HELLO()'\'''
# End Whenever generated tasks for: myAPP
myModel -> HELLO()
def HELLO()
p "HELLO"
end
How do I know if this is working? I don't think it is because the logs are blank. What am I doing wrong?
thanks
p "HELLO" just prints to stdout, which doesn't do much good in a cronjob context. Try this code instead:
def HELLO()
Rails.logger.info "HELLO"
end
This will print the message to the standard Rails log file. By default, this will be logs/production.log in the current release that Capistrano deployed.

How long should "rake routes" run?

I just started out with Rails, so excuse my fairly basic question. I am already noticing that the rake routes command takes a while to execute everytime I run it. I have about 20 routes for 3 controllers and it takes about 40 seconds to execute.
Is that normal? How could I speed this up?
P.S.: I am on Windows 7 with Rails 3.1.3 (set up with Rails Installer).
The rake routes task depends on the environment task which loads your Rails environment and requires thousands of Ruby files.
The startup time of a Rails environment and the corresponding rake routes execution time are very close (on my Linux on-steroids-laptop with a Rails application with ~ 50 routes):
$ time ruby -r./config/environment.rb -e ''
real 0m5.065s
user 0m4.552s
sys 0m0.456s
$ time rake routes
real 0m4.955s
user 0m4.580s
sys 0m0.344s
There is no easy way to decrease startup time as it relies on the way your interpreter requires script files : http://rhnh.net/2011/05/28/speeding-up-rails-startup-time
I came up with a solution to rake routes taking about 8 seconds to run every time. It's a simple file based cache that runs bundle exec rake routes, stores the output in a file under tmp. The filename is the md5 hash of config/routes.rb, so if you make a change and change it back, it will use the old cached file.
I put the following bash functions in an executable file I call fastroutes:
if [ ! -f config/routes.rb ]; then
echo "Not in root of rails app"
exit 1
fi
cached_routes_filename="tmp/cached_routes_$(md5 -q config/routes.rb).txt"
function cache_routes {
bundle exec rake routes > $cached_routes_filename
}
function clear_cache {
for old_file in $(ls tmp/cache_routes*.txt); do
rm $old_file
done
}
function show_cache {
cat $cached_routes_filename
}
function show_current_filename {
echo $cached_routes_filename
}
function main {
if [ ! -f $cached_routes_filename ]; then
cache_routes
fi
show_cache
}
if [[ "$1" == "-f" ]]
then
show_current_filename
elif [[ "$1" == "-r" ]]
then
rm $cached_routes_filename
cache_routes
else
main
fi
Here's a github link too.
This way, you only have to generate the routes once, and then fastroutes will used the cached values.
That seems a bit long, but do you really need to run rake routes that often? On my system, OSX Lion/Rails 3.2.0, rake routes takes ~10s.
In your Rakefile:
#Ouptut stored output of rake routes
task :fast_routes => 'tmp/routes_output' do |t|
sh 'cat', t.source
end
#Update tmp/routes_output if its older than 'config/routes.rb'
file 'tmp/routes_output' => 'config/routes.rb' do |t|
outputf = File.open(t.name, 'w')
begin
$stdout = outputf
Rake.application['routes'].invoke
ensure
outputf.close
$stdout = STDOUT
end
end
Rails environment takes a huge more amount of time to be loaded on Windows. I recommend you to give Unix a try, like Ubuntu, as Windows is the worst environment in which you can run and develop Ruby on Rails applications. But if you are just trying Rails, Windows is enough :)

Capsitrano deploy recipe : after deploy file listing and modifications

I'm currently working on a multi-stage recipe for Capistrano that would, ideally, after deploy, make wise use of the yui compressor for all css and js.
Here's what I currently came up to :
after "deploy", "deploy:cleanup", "minifier:compress"
# Task to minify via Yui-compressor
# Uses compressor bundled with application in #{application}/lib/yuicompressor
namespace :minifier do
def minify(files)
files.each do |file|
cmd = "java -jar lib/yuicompressor/build/yuicompressor-2.4.6.jar #{file} -o #{file}"
puts cmd
ret = system(cmd)
raise "Minification failed for #{file}" if !ret
end
end
desc "minify"
task :compress do
minify_js
minify_css
end
desc "minify javascript"
task :minify_js do
minify(Filelist['public/js/**/*.js'])
end
desc "minify css"
task :minify_css do
minify(Filelist['public/css/**/*.css'])
end
end
What's really puzzling me is the
uninitialized constant Capistrano::Configuration::Filelist (NameError)
I get as soon as Capistrano gets to the point.
As a total newbie to Ruby, Rails, and Capistrano, I understand for some reason FileList isn't a common Capistrano method, but can't figure out what to replace it with.
Thanks for the help.
Your task is conceptually wrong, it will run on local system (the one from which you're deploying), because you're call system, you should use the run method which run commands remotely.
def minify(files)
files.each do |file|
cmd = "java -jar lib/yuicompressor/build/yuicompressor-2.4.6.jar #{file} -o #{file}"
puts cmd
ret = system(cmd) # *** SYSTEM RUN LOCAL COMMANDS ***
raise "Minification failed for #{file}" if !ret
end
end
That said, I will change that code with shell scripting, something like (untested)
task :minify
cmd = "java -jar lib/yuicompressor/build/yuicompressor-2.4.6.jar"
run "find #{current_path}/public/css/ -name '*.css' -print0 | xargs -0 -I file #{cmd} file -o file"
run "find #{current_path}/public/js/ -name '*.js' -print0 | xargs -0 -I file #{cmd} file -o file"
end
or if you prefer to use ruby to program it, you should move the code into a rake task (which you can try and debug locally) and then invoke it with Capistrano: How do I run a rake task from Capistrano?

Is there a way to use capistrano (or similar) to remotely interact with rails console

I'm loving how capistrano has simplified my deployment workflow, but often times a pushed change will run into issues that I need to log into the server to troubleshoot via the console.
Is there a way to use capistrano or another remote administration tool to interact with the rails console on a server from your local terminal?
**Update:
cap shell seems promising, but it hangs when you try to start the console:
cap> cd /path/to/application/current
cap> pwd
** [out :: application.com] /path/to/application/current
cap> rails c production
** [out :: application.com] Loading production environment (Rails 3.0.0)
** [out :: application.com] Switch to inspect mode.
if you know a workaround for this, that'd be great
I found pretty nice solution based on https://github.com/codesnik/rails-recipes/blob/master/lib/rails-recipes/console.rb
desc "Remote console"
task :console, :roles => :app do
env = stage || "production"
server = find_servers(:roles => [:app]).first
run_with_tty server, %W( ./script/rails console #{env} )
end
desc "Remote dbconsole"
task :dbconsole, :roles => :app do
env = stage || "production"
server = find_servers(:roles => [:app]).first
run_with_tty server, %W( ./script/rails dbconsole #{env} )
end
def run_with_tty(server, cmd)
# looks like total pizdets
command = []
command += %W( ssh -t #{gateway} -l #{self[:gateway_user] || self[:user]} ) if self[:gateway]
command += %W( ssh -t )
command += %W( -p #{server.port}) if server.port
command += %W( -l #{user} #{server.host} )
command += %W( cd #{current_path} )
# have to escape this once if running via double ssh
command += [self[:gateway] ? '\&\&' : '&&']
command += Array(cmd)
system *command
end
This is how i do that without Capistrano: https://github.com/mcasimir/remoting (a deployment tool built on top of rake tasks). I've added a task to the README to open a remote console on the server:
# remote.rake
namespace :remote do
desc "Open rails console on server"
task :console do
require 'remoting/task'
remote('console', config.login, :interactive => true) do
cd config.dest
source '$HOME/.rvm/scripts/rvm'
bundle :exec, "rails c production"
end
end
end
Than i can run
$ rake remote:console
I really like the "just use the existing tools" approach displayed in this gist. It simply uses the SSH shell command instead of implementing an interactive SSH shell yourself, which may break any time irb changes it's default prompt, you need to switch users or any other crazy thing happens.
Not necessarily the best option, but I hacked the following together for this problem in our project:
task :remote_cmd do
cmd = fetch(:cmd)
puts `#{current_path}/script/console << EOF\r\n#{cmd}\r\n EOF`
end
To use it, I just use:
cap remote_cmd -s cmd="a = 1; b = 2; puts a+b"
(note: If you use Rails 3, you will have to change script/console above to rails console, however this has not been tested since I don't use Rails 3 on our project yet)
cap -T
cap invoke # Invoke a single command on the remote ser...
cap shell # Begin an interactive Capistrano session.
cap -e invoke
------------------------------------------------------------
cap invoke
------------------------------------------------------------
Invoke a single command on the remote servers. This is useful for performing
one-off commands that may not require a full task to be written for them. Simply
specify the command to execute via the COMMAND environment variable. To execute
the command only on certain roles, specify the ROLES environment variable as a
comma-delimited list of role names. Alternatively, you can specify the HOSTS
environment variable as a comma-delimited list of hostnames to execute the task
on those hosts, explicitly. Lastly, if you want to execute the command via sudo,
specify a non-empty value for the SUDO environment variable.
Sample usage:
$ cap COMMAND=uptime HOSTS=foo.capistano.test invoke
$ cap ROLES=app,web SUDO=1 COMMAND="tail -f /var/log/messages" invoke
The article http://errtheblog.com/posts/19-streaming-capistrano has a great solution for this. I just made a minor change so that it works in multiple server setup.
desc "open remote console (only on the last machine from the :app roles)"
task :console, :roles => :app do
server = find_servers_for_task(current_task).last
input = ''
run "cd #{current_path} && ./script/console #{rails_env}", :hosts => server.host do |channel, stream, data|
next if data.chomp == input.chomp || data.chomp == ''
print data
channel.send_data(input = $stdin.gets) if data =~ /^(>|\?)>/
end
end
the terminal you get is not really amazing though. If someone have some improvement that would make CTRL-D and CTRL-H or arrows working, please post it.

Resources