RR to RSpec Converter
This is a modification of the Rails5 Spec Converter tool to handle converting from RR v1.1.2 to RSpec v3.6.0 syntax for mocking and stubbing. This is a basic implementation to handle the needs of a large codebase of 6000+ tests. It handles most usages but is not exhaustive.
- Installation
- Usage
- Development
- RR
- Mocking and Stubbing
- #mock
- #mock!
- #stub
- #stub!
- #dont_allow
- #dont_allow!
- #proxy
- #proxy!
- #instance_of
- #instance_of!
- #any_instance_of
- #with_any_args
- #with_no_args
- #times
- #any_times
- #returns
- #yields
- #with
- #never
- #once
- #at_least
- #at_most
- Matchers
- #anything
- #is_a
- #numeric
- #boolean
- #duck_type
- #hash_including
- #rr_satisfy
- Module References
- RR::WildcardMatchers::HashIncluding
- RR::WildcardMatchers::Satisfy
- RR.reset
- Mocking and Stubbing
- Manual Cleanup
- Configure RSpec
- License
Installation
Install the gem standalone like so:
$ gem install rr-to-rspec-converter
Usage
Make sure you've committed everything to Git first, then
$ cd some-project
$ rr-to-rspec-converter
This will update all the files in that directory matching the globs spec/**/*_spec.rb
or test/**/*_test.rb
.
If you want to specify a specific set of files instead, you can run rr-to-rspec-converter path_to_my_files
.
By default it will make some noise, run with rr-to-rspec-converter --quiet
if you want it not to.
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run bundle exec rspec spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
RR
The following lists each RR API method and the conversion that is applied. Some cases are not handled. To see which ones, search for unhandled
in this document.
Mocking and Stubbing
#mock
mock(view).render.with_any_args.twice do |*args|
if args.first == {:partial => "user_info"}
"User Info"
else
"Stuff in the view #{args.inspect}"
end
end
=> expect(object).to receive(:method).with(arguments).and_return(return_val)
#mock!
Unhandled
Nested usage:
mock.something.mock!.method { result }
=> and_return(double(method: result))
Simple usage:
mock!
=>
double
instance_double("ConsoleNotifier")
class_double("ConsoleNotifier")
object_double(User.new, :save => true)
object_double("MyApp::LOGGER", :info => nil).as_stubbed_const
Then assert expectations with `have_received` or `receive`.
#stub
By itself:
x = stub
=> double
With method:
x = stub(object).method.with(args).returns { return_val }
=> allow(object).to receive(:method).with(arguments).and_return(return_val)
#stub!
Unhandled
=>
instance_double("ConsoleNotifier")
class_double("ConsoleNotifier")
object_double(User.new, :save => true)
object_double("MyApp::LOGGER", :info => nil).as_stubbed_const
#dont_allow
Normal usage:
dont_allow(object).method(args)
=> expect(object).not_to receive(:method).with(args)
Inside any_instance_of
block (Unhandled):
any_instance_of(klass) do |o|
dont_allow(object).method(args)
end
=> expect_any_instance_of(klass).not_to receive(:method).with(args)
#dont_allow!
Unhandled
#proxy
Unhandled
=>
and_call_original, or
and_wrap_original
allow(Calculator).to receive(:add).and_call_original
allow(Calculator).to receive(:add).with(2, 3).and_return(-5)
expect(API).to receive(:solve_for).and_wrap_original { |m, *args| m.call(*args).first(5) }
expect(API.solve_for(100)).to eq [1,2,3,4,5]
#proxy!
Unhandled
#instance_of
=> instance_of
#instance_of!
Unhandled
#any_instance_of
any_instance_of(User) do |u|
stub(u).valid? { false }
end
or
any_instance_of(User, :valid? => false)
or
any_instance_of(User, :valid? => lambda { false })
=>
allow_any_instance_of(klass).to receive_messages(:method => return_value)
expect_any_instance_of(klass).to receive_messages(:method => return_value)
allow_any_instance_of(klass).to receive(:method => return_value)
expect_any_instance_of(klass).to receive(:method => return_value)
#with_any_args
mock(PetitionGroup).find.with_any_args.returns(petition_group)
=> with(any_args)
#with_no_args
=> with(no_args)
#times
=>
times(0) => expect().not_to receive()
times(1) => once
times(2) => twice
times(n) => exactly(n).times **Unhandled**
times(any_times) => allow().to receive() OR at_least(:once)
#any_times
Converts to at_least(:once)
but this interpretation may be incorrect. The intention of the test may be to allow
the message to be received - in which case mock
should not have been used.
.any_times
.times(any_times)
=> allow().to receive() OR at_least(:once)
#returns
mock(PetitionGroup).find.with_any_args.returns(petition_group)
=> and_return
#yields
Unhandled
#with
=> with
#never
=> expect(object).not_to receive(:method)
#once
=> once
#at_least
=>
at_least(0) => Use `allow` instead of `expect` **Unhandled**
at_least(1) => at_least(:once)
at_least(2) => at_least(:twice)
at_least(n) => at_least(n).times
#at_most
=>
at_most(1) => at_most(:once)
at_most(2) => at_most(:twice)
at_most(n) => at_most(n).times
Matchers
#anything
=> anything
#is_a
=> kind_of
#numeric
=> kind_of(Numeric)
#boolean
=> boolean
#duck_type
=> duck_type
#hash_including
=> hash_including
#rr_satisfy
Use RSpec satisfy
method. Would need to rewrite to use arguments from block, which alters the intent of the test. Will probably require manual fixes post conversion.
=> satisfy
Module References
RR::WildcardMatchers::HashIncluding
=> hash_including
RR::WildcardMatchers::Satisfy
=> rr_satisfy
RR.reset
=> removes line
Manual Cleanup
- Remove all comments that reference
/\brr\b/i
- Remove 'rr' from Gemfile
- Remove
config.mock_with :rr
fromspec/spec_helper.rb
Configure RSpec
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
License
The gem is available as open source under the terms of the MIT License.