RSpec::Spy
This gem allows you to write mock expectations in an AAA (Arrange-Act-Assert) fashion
with RSpec::Mocks. It does this by allowing you to declare examples with :spy => true
or :spy (if you use config.treat_symbols_as_metadata_keys_with_true_values = true)
so that they are effectively executed before everything else. Here's a simple example:
describe "Example" do
let(:collaborator) { stub.as_null_object }
before do
collaborator.message
end
it "should receive a message", :spy => true do
collaborator.should_have_received :message
end
it "should not receive other_message", :spy => true do
collaborator.should_not_have_received :other_message
end
endInstallation
Add this line to your application's Gemfile in the :test group
gem 'rspec-spy'
And then execute:
$ bundle
Or install it yourself as:
$ gem install rspec-spy
Add to your spec_helper.rb in RSpec.configure AFTER any other global before hooks, especially DatabaseCleaner:
# any config.before hooks should go above
require 'rspec-spy'
config.before(:each, :spy => true) do |example|
RSpec::Spy(example)
endUsage
Just tag your examples with :spy => true or :spy (if you use
config.treat_symbols_as_metadata_keys_with_true_values = true)
You should be able to use all of the functionality from rspec-mocks that you're
used to, including spying on class methods.
it "should receive message", :spy => true do
collaborator.should_have_received :message
end
# with config.treat_symbols_as_metadata_keys_with_true_values = true
it "should receive message", :spy do
collaborator.should_have_received :message
endWarnings
- This is a hack, you'll want to make sure everyone on your team is aware of the behavior and these warnings.
- You should probably avoid instance vars and stick to
let, it gets confusing because you cannot set them inbeforeblocks and use them inspyblocks. Remember,spyblocks actually happen beforebeforeblocks. Example (see the example at the beginning of the readme for the right way):
describe "what not to do" do
before do
@collaborator = stub.as_null_object
@collaborator.message
end
# These will fail because @collaborator is nil because this happens
# before the above before block
it "should receive a message", :spy => true do
@collaborator.should_have_received :message
end
it "should not receive other_message", :spy => true do
@collaborator.should_not_have_received :other_message
end
end- If your tests depend on the method you are spying on returning something then you'll
need to use
and_returnin yourspyblock and if you have normal examples you'll also need tostubit. Yes, this is annoying, but that's how rspec-mocks works and it's one of the many reasons you shoudln't mock when the return value matters (juststub). Example:
describe "stubbing and mocking at the same time" do
let(:collaborator) { stub.as_null_object }
before do
collaborator.stub(:message) { 5 }
@result = collaborator.message
# This will fail unless you use and_return
@result.should == 5
end
it "should return 5" do
# This will fail unless you stub in before
@result.should == 5
end
it "should receive a message", :spy => true do
collaborator.should_have_received(:message).and_return(5)
end
endAlternatives
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Added some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request