Automatically run RSPec when plain-old Ruby (not Rails) files change
Like David said, Guard can be used to watch a wide variety of files and perform actions when those files are modified. It does not have to be used with a Rails app. I have set up something similar in the past using guard
. Here is what I did:
Place the following in your Gemfile
:
source 'https://rubygems.org'
gem 'guard'
gem 'guard-shell'
gem 'rspec'
gem 'rb-fsevent', '~> 0.9'
Then run:$ bundle install
Create a Guardfile in your home directory with:
$ guard init
In the Guardfile
, comment out the examples and add this:
guard :shell do
watch(%r{^*\.rb}) { `bundle exec rspec spec/` }
end
This tells guard to watch for modifications to any ruby files in the directory and execute the command bundle exec rspec spec/
when they change (the backticks are used to execute the command in the shell).Then open up a new terminal window in your current directory and start a guard
server to start watching the files:
$ bundle exec guard
Now your Rspec test suite should automatically run when you modify ruby files in the directory.
Tests do not run when file changes with Guard and rspec on windows
I'm not a Windows user and this is not from my personal experience, but as a Guard maintainer I often hear that the fchange gem has its problems. You can force Guard polling for changes and thus skip fchange with:
bundle exec guard -p
Maher Sallam has addressed this issue and created wdm, which seems to be more reliable. There's also a pull request for integrate it into Guard. I recommend you to test the integration and give Maher some feedback from a real world Windows user, since Maher is a Linux guy :P
Questions about setting up a working Guard-RSpec example project
I'll answer the question in the title and cover the "sub-questions" as I go.
The best way to set up a project (as of now) is to use
bundle exec guard --init rspec
. This should give reasonable defaults to start work immediately. It's not perfect, but there is a lot of work planed to improve things, so do ask questions on GitHub instead (makes more sense).Guard has to maintain backward compatibility, so there are some "non-intuitive" things for now and skimming through all the docs first (and wiki) is a good investment to quickly know what's available and where. Asking when confused or in doubt is also a good idea (one issue per question is best).
Guard uses a DSL to simplify setting up listeners. This is a bit non-intuitive and clunky at times, but it does help organize actions into groups, which can help maintain complex workflows. The DSL's command for e.g. clearing the terminal automatically is: (
clearing: on
). (This answers question 3).There are all kinds of uses and scenarios, so Guard tries hard to make everyone happy and on every platform too. Guard uses Listen, which watches directories recursively (due to restrictions on OSX mostly). This usually isn't a problem, but for huge directions on OSX this can be very, very slow. That's why Guard lets you select which top directories to watch (like 'lib', 'app', etc.). Watching the whole project directory is very convenient, so that is still the default. More info on this is at the Listen project. So by default all directories (
:directories
statement) are "physically watched" (take up operating resources), though in Guard you only configure which changes you want to respond to (which is whatwatch
statements do).The words chosen are a bit misleading at times. E.g.
watch
in the DSL actually means: "out of all the changes happening, select changes matching ...". The block passed towatch
is given the results of the match. That block should return a list of files for the current guard plugin to run. So "watch" probably is more misleading than helpful. "match" would probably be a better choice and it might replace "watch" in the future.The guard-rspec project runs RSpec on changed files. You can see the exact RSpec commands if you run guard with the debugging option on, e.g.
bundle exec guard -d
. To simplify setup, Guard::RSpec uses a DSL that should work out-of-the-box if your project follows a given setup. E.g.dsl.watch_spec_files_for(ruby.lib_files)
is defined here: so it pretty much already should do what you want if you put all your tested source files inlib
. For other folders, you can add your own. E.g. Rails projects typically have sources also in anapp
directory, so in the default Guard::RSpec template there's an statement:dsl.watch_spec_files_for(rails.app_files)
with the pattern defined as:rails.app_files = %r{^app/(.+)\.rb$}
If you have a typical case that isn't covered, open an issue in that project. (covers question 2).
- Instead of
let (:greeter) { Example::RSpecGreeter.new }
, I'd use RSpec's implicit subject (but some may argue that it's less explicit and less readable) - if you're testing
Example::RSpecGreeter
, I'd recommend putting that class in a separate file and including it instead (require 'example/rspec_greeter'
) - you might add RubCop/Guard::RuboCop for detecting convention issues (covers 4th question)
- you might want to check out Guard's own Guardfile used to test itself for a more "real-life" setup without the documentation clutter.
- for best results I think it's best to copy a whole existing project and rename the parts you want to change. Usually there are many hours spent still fine-tuning things like alternate RSpec configs for Travis, special gem release tasks, certain workarounds and conveniences, etc.
Set up RSpec to test a gem (not Rails)
I've updated this answer to match current best practices:
Bundler supports gem development perfectly. If you are creating a gem, the only thing you need to have in your Gemfile is the following:
source "https://rubygems.org"
gemspec
This tells Bundler to look inside your gemspec file for the dependencies when you run bundle install
.Next up, make sure that RSpec is a development dependency of your gem. Edit the gemspec so it reads:
spec.add_development_dependency "rspec"
Next, create spec/spec_helper.rb
and add something like:require 'bundler/setup'
Bundler.setup
require 'your_gem_name' # and any other gems you need
RSpec.configure do |config|
# some (optional) config here
end
The first two lines tell Bundler to load only the gems inside your gemspec. When you install your own gem on your own machine, this will force your specs to use your current code, not the version you have installed separately.Create a spec, for example spec/foobar_spec.rb
:
require 'spec_helper'
describe Foobar do
pending "write it"
end
Optional: add a .rspec
file for default options and put it in your gem's root path:--color
--format documentation
Finally: run the specs:$ rspec spec/foobar_spec.rb
rspec - bug when I try to run multiple files including one that has a line-number?
Upgrade to a newer version of rspec, for example version 2.14.8+
How do you run a single test/spec file in RSpec?
Or you can skip rake and use the 'rspec' command:
bundle exec rspec path/to/spec/file.rb
In your case I think as long as your ./spec/db_spec.rb file includes the appropriate helpers, it should work fine.If you're using an older version of rspec it is:
bundle exec spec path/to/spec/file.rb
rspec filter with tag - do not run code in `describe`
My final solution is not elegant, but get job done effectively (thanks @noname for pointing me for direction of solution)
I create a new rake task, that filter files with specified tag and after that create subprocess to run only filtered specs (and like rspec --fail-fast
fails on first failure (as I need it)
like this
files_with_tag = `grep -Ril spec/ -e ':mytag'`.split("\n")
files_with_tag.each do |current_file|
result = `rspec #{current_file} --tag mytag`
puts(result)
exit unless result.include?('0 failures')
end
Related Topics
Why Does Array.To_S Return Brackets
Multistep Form with Activeadmin
How to Create Line Breaks in Ruby
Sorting Hash of Hashes by Value (And Return The Hash, Not an Array)
Gitlab Configuration Issues:: Nginx Unicorn Port Conflict
Ruby Require 'File' Doesn't Work But Require './File' Does. Why
How to Install Wraith on Windows 7
Timeout When Installing Ruby Gems
Why Do Ruby People Say They Don't Need Interfaces
Rails Has_Many Through Form with Additional Attributes
Access an Instance Variable from Child Classes
Obtaining Number of Block Parameters
Should I Move My Custom Methods to Model from Controller
How to Make Http Delete Request in My Ruby Code Using Net::Http
How to Host Gem in Github and Use It