Wednesday, November 4, 2009

Getting Testjour up and running - part 3

We were ultimately able to reduce our entire test suite runtime from 5-12 minutes depending on hardware down to less than 3 1/2 minutes consistently. Now all our developers can test efficiently and we can scale as our test suite grows. We used a combination of TestJour, Capistrano, and Git to accomplish this very effectively.

Using Testjour with Capistrano

I created the following capistrano recipe excerpt and loaded it in our main deploy file. We have our deploy separated between staging and production so naturally adding a testing deploy made sense. We can now run all our features in a distributed fashion using only:

cap testing deploy

To run with migrations we do:

cap testing deploy -s migrations=true

To run with migrations and less than a full test suite we do:

cap testing deploy -s migrations=true -s features=./features/subfeatures

This recipe excerpt requires a config/testjour.yml configuration file in the following format:

config/testjour.yml
master: testmaster.domain.tld
slaves: testslave1.domain.tld,testslave2.domain.tld,testslave3.domain.tld,testslave4.domain.tld,testslave5.domain.tld,testslave6.domain.tld,testslave7.domain.tld
repository: "ssh://user@testmaster.domain.tld/user/myapp.git"
master_user: user
root_path: /user
dbusername: masterdbusername
dbpassword: masterdbpassword

lib/testjour.rb


TestJour_config=Hash.new
TestJour_config.merge! YAML.load_file("config/testjour.yml") if File.exists?("config/testjour.yml")

task :testing do
role :app, testjour_master
role :web, testjour_master
role :db, testjour_master, :primary => true
set :application, "myapp"
set :repository, TestJour_config["repository"]
set :rails_env, 'test'
set :branch, get_branch
set :user, TestJour_config["master_user"]
set :deploy_to, "#{TestJour_config["root_path"]}/#{application}"

before :deploy, :role => :app do
`git push testjour #{get_branch}`
end

after :deploy, :role => :app do
begin
if migrations
run "cd #{current_path}; rake db:migrate:reset"
run "cd #{current_path}; mysqldump#{TestJour_config["dbusername"] ? " -u " + TestJour_config["dbusername"] : ""}#{TestJour_config["dbpassword"] ? " --password=" + TestJour_config["dbpassword"] : ""} -n -d myapp_development > #{shared_path}/development_structure.sql"
end
rescue
end

run "/bin/cp #{shared_path}/development_structure.sql #{release_path}/db/development_structure.sql"
run "cd #{current_path}; testjour #{get_slaves} --max-local-slaves=1 --create-mysql-db --mysql-db-name=myapp_test #{get_features}"
end
end


def get_branch
`git branch` =~ /^\*.(.*)/
$1
end


def testjour_master
TestJour_config["master"]
end


def get_features
begin
features
rescue
"./features"
end
end


def get_slaves
TestJour_config["slaves"].split(',').map{slave "--on=testjour://#{slave}/#{TestJour_config['root_path']}/#{application}/current/ "}.to_s.strip if TestJour_config["slaves"]
end



Back to part 2

1 comment:

  1. Hello,

    Thank you for this detailed write-up - I was just starting to look into testjour myself, and was about to have to do the same thing - figure out manually how it works.

    Can you say a word or two about how it divides your features across the available slaves so that different slaves don't end up running the same features? How effective is it at doing this?

    Thanks,
    Avram

    ReplyDelete