Flatware
Flatware parallelizes your test suite to significantly reduce test time.
Flatware
Add the runners you need to your Gemfile:
gem 'flatware-rspec', require: false # one
gem 'flatware-cucumber', require: false # or both
then run
bundle install
Usage
Cucumber
To run your entire suite with the default cucumber options, add the flatware-cucumber
gem and just:
$ flatware cucumber
RSpec
To run your entire suite with the default rspec options add the flatware-rspec
gem and just:
$ flatware rspec
The rspec runner can balance worker loads, making your suite even faster.
It forms balaced groups of spec files according to their last run times, if you've set example_status_persistence_file_path
in your RSpec config.
For this to work the configuration option must be loaded before any specs are run. The .rspec
file is one way to achive this:
--require spec_helper
But beware, if you're using ActiveRecord in your suite you'll need to avoid doing things that cause it to establish a database connection in spec_helper.rb
. If ActiveRecord connects before flatware forks off workers, each will die messily. All of this will just work if you're following the recomended pattern of splitting your helpers into spec_helper
and rails_helper
. Another option is to use the configurable hooks.
Options
If you'd like to limit the number of forked workers, you can pass the 'w' flag:
$ flatware -w 3
You can also pass most cucumber/rspec options to Flatware. For example, to run only features that are not tagged 'javascript', you can:
$ flatware cucumber -t 'not @javascript'
Additionally, for either cucumber or rspec you can specify a directory:
$ flatware rspec spec/features
Typical Usage in a Rails App
Add the following to your config/database.yml
:
test:
database: foo_test
becomes:
test:
database: foo_test<%=ENV['TEST_ENV_NUMBER']%>
Run the following:
$ rake db:setup # if not already done
$ flatware fan rake db:test:prepare
Now you are ready to rock:
$ flatware rspec && flatware cucumber
Faster Startup With ActiveRecord
Flatware has a couple lifecycle callbacks that you can use to avoid booting your app
over again on every core. One way to take advantage of this via a spec/flatware_helper.rb
file like so:
Flatware.configure do |conf|
conf.before_fork do
require 'rails_helper'
ActiveRecord::Base.connection.disconnect!
end
conf.after_fork do |test_env_number|
config = ActiveRecord::Base.connection_db_config.configuration_hash
ActiveRecord::Base.establish_connection(
config.merge(
database: config.fetch(:database) + test_env_number.to_s
)
)
end
end
Now when I run bundle exec flatware rspec -r ./spec/flatware_helper
My app only boots once, rather than once per core.
Design Goals
Maintainable
- Fully test at an integration level. Don't be afraid to change the code. If you break it you'll know.
- Couple as loosely as possible, and only to the most stable/public bits of Cucumber and RSpec.
Minimal
- Projects define their own preparation scripts
- Only distribute to local cores (for now)
Robust
- Depend on a dedicated messaging library
- Be accountable for completed work; provide progress report regardless of completing the suite.
Tinkering
Flatware integration tests use aruba. In order to get a demo cucumber project you
can add the @no-clobber
tag to features/flatware.feature
and run the test
with cucumber features/flatware.feature
. Now you should have a ./tmp/aruba
directory. CD there and flatware
will be in your path so you can tinker away.
How it works
Flatware relies on a message passing system to enable concurrency. The main process forks a worker for each cpu in the computer. These workers are each given a chunk of the tests to run. The workers report back to the main process about their progress. The main process prints those progress messages. When the last worker is finished the main process prints the results.
Resources
Contributing to Flatware
Do whatever you want. I'd love to help make sure Flatware meets your needs.
About
Flatware is supported by the team at Hashrocket, a multidisciplinary design & development consultancy. If you'd like to work with us or join our team, don't hesitate to get in touch.