How to stub download from remote url in Carrierwave - ruby-on-rails

I have a User AR model and when I save a User instance with a populated value of remote_avatar_url, Carrierwave automatically downloads the avatar. More info about this feature here.
Now, in my tests, I want to stub this behavior. I know I can do:
allow_any_instance_of(UserAvatarUploader).to receive(:download!)
however, the rspec-mocks documentation discourages the use of allow/expect_any_instance_of.
What is the proper way of stubbing this specific feature of Carrierwave in tests?
P.S. I have already disabled image processing in tests:
config.enable_processing = false if Rails.env.test?

For me, the answer is to use the webmock gem. It blocks outbound HTTP connections during testing and allows you to easily stub responses.
After setting up the gem per the instructions, I added this to my tests:
body_file = File.open(File.expand_path('./spec/fixtures/attachments/sample.jpg'))
stub_request(:get, 'www.thedomainofmyimage.example.net').
to_return(body: body_file, status: 200)
Worked like a charm with CarrierWave's remote_<uploader>_url feature.

Related

using VCR with Rspec in feature scenarios

I have a Rails 4 app that uses a custom authentication gem that authenticates users against a third-party API. The app requires authentication for most actions on the site (visitors can do very little).
I am trying to use VCR to record the api request made during authentication for all of the integration tests, but all examples that I can find on SO and the Relish documentation only cover how to do this with Rspec in a 'describe do' spec, as referenced here:
https://www.relishapp.com/vcr/vcr/v/1-6-0/docs/test-frameworks/usage-with-rspec
Since no customers are involved on this project, I am writing integration tests with Rspec and Capybara instead of Cucumber, so my tests are using the 'feature/scenario' format like so:
feature 'posts' do
scenario 'a user can log in' do
# use vcr for api request
sign_in_user # refers to a method that handles the api call to log in a user, which is what I would like VCR to record.
expect(page).to have_content("User signed in successfully")
end
end
Using the command described in the documentation:
use_vcr_cassette
inside of the 'scenario' block, returns an error:
Failure/Error: use_vcr_cassette
undefined local variable or method `use_vcr_cassette' for #<RSpec::ExampleGroups::Posts:0x007fb858369c38>
I followed the documentation to setup VCR in my spec/rails_helper.rb (which is included by the spec/spec_helper.rb)... which basically looks like this:
require 'vcr'
VCR.configure do |c|
c.cassette_library_dir = 'support/vcr_cassettes'
c.hook_into :webmock
end
Obviously added gem 'vcr' to my Gemfile development/test group and it is a thing in console and binding.pry from inside of a test.
Has anyone used VCR inside of a Rspec feature? or have any suggestions on what I might do as a workaround?
Thanks in advance
Solution: Taryn East got me to the solution, but it is slightly different than the link posted for anyone trying to do this moving forward.
here is the most basic config in spec/rails_helper.rb or spec/spec_helper.rb:
require 'vcr'
VCR.configure do |c|
c.cassette_library_dir = 'spec/cassettes'
c.hook_into :webmock
c.configure_rspec_metadata!
end
using c.configure_rspec_metadata! is required for Rspec to handle the :vcr tag.
And in an Rspec Feature spec:
feature 'users' do
scenario 'logged in users should be able to do stuff', :vcr do
# authenticate user or make other http request here
end
end
Oddly enough, in my tests - VCR is recording the response and if passes the first time, but fails the second time. I traced this to the response being stored differently than it is received.
On a normal request (using excon) like so:
resp = Excon.post(url, :body => data, :headers => { "Content-Type" => "application/x-www-form-urlencoded", "Authorization" => authorization_header })
The response has a header that is accessible in this format:
resp.headers["oauth_token"]
which returns an oauth token.
In the VCR response, it is being stored differently and only accessible as:
resp.headers["Oauth-Token"]
Which is weird, but workable. This may be a bug with VCR or some issue with Excon... too busy to figure that one out right now, but just a heads up in case anyone else uses this setup and gets a passing test with the live http request and a failing test when using the VCR cassette. A quick workaround is to either change the VCR cassette data to match what your code expects, or modify your code to accept either available value.

Restricting VCR to specs

Is there a way to make VCR active only when called via rspec in a Rails app? It works great for my tests, but I don't want it to intercept requests when outside of those tests.
I get Real HTTP connections are disabled if I use a client to connect to the my app and the app is calling an external web service.
Thanks,
Marc
make sure that your VCR gem is set in the proper group
# Gemfile
group :test do
......
gem 'vcr'
end
take a look at http://natashatherobot.com/vcr-gem-rails-rspec/ for more help.
I finally got this work as desired with Rails. I was able to disable VCR (WebMock, actually, which is the backend I chose for VCR) except when I'm running rspec. For background, I initially followed the instructions here when setting up VCR.
First, I create config/initializers/webmock.rb:
# Disable WebMock globally so it doesn't interfere with calls outside of the specs.
WebMock.disable!
Then I added the following around VCR.use_cassette() (in my case this is in spec_helper.rb:
config.around(:each, :vcr) do |example|
name = example.metadata[:full_description].split(/\s+/, 2).join("/").underscore.gsub(/[^\w\/]+/, "_")
options = example.metadata.slice(:record, :match_requests_on).except(:example_group)
+ # Only enable WebMock for the specs. Don't interfere with calls outside of this.
+ WebMock.enable!
VCR.use_cassette(name, options) { example.call }
+ WebMock.disable!
end
Hope that helps someone.

Fake call to a web service in all cucumber tests

My Rails app makes a call to a web service.
I am using the FakeWeb gem to fake these calls in some tests by registering the URI like this:
FakeWeb.register_uri(:get, "http://webservice.com/param?value=a", :response => fake_response)
How can I set this fake registration for the entire test environment, as opposed to setting it up manually for each test ?
Most likely, putting that in your spec/spec_helper.rb file will do the trick. I haven't used FakeWeb, myself, but that's where you'd put any global initialization.
Take look at VCR rubygem.
It records your test suite's HTTP interactions and replays them during test run.
In a file in spec/support/, make a call to register_uri inside a Before block:
Before do
FakeWeb.register_uri(:get, "http://webservice.com/param?value=a", :response => fake_response)
end
I use this trick to set up mock responses using webmock, and it works like a charm.

How to stub in Rails development environment?

I'm looking for a reliable way to dynamically stub certain methods in my development environment. One example use case is when I need to do development that normally requires access to the Facebook Graph APIs but I don't have Internet access. I'd like to be able to stub the calls to fb_graph methods so it looks as if I'm authenticated and have profile data. Ideally I could turn the stubs on or off with a minor config change.
Any ideas? I'm assuming something like mocha can handle this?
You can use the VCR gem which will record the results of an initial HTTP request into a yml file and then use the contents of that yml file on subsequent http requests. It can then be configured to ignore the VCR logic and always make HTTP requests, if so desired:
https://www.relishapp.com/myronmarston/vcr
Mocha can certainly do it. But it feels a bit strange.
You could also do something like dependency injection.
For instance:
class User < AR::Base
def find_friends
Facebook.find_friends(facebook_id)
end
end
class Facebook
def self.find_friends(id)
# connect to Facebook here
end
end
class FakeFacebook
def self.find_friends(id)
# a fake implementation here
end
end
And inside an initializer:
if Rails.env.development?
User::Facebook = FakeFacebook
end

How to stub carrierwave in Rspec?

I want to stub carrierwave to prevent it from fetch images on the web during my tests. How would I stub things to achieve this?
My crawler parses a remote web page, and saves one image url into the model. Carrierwave will fetch that image automatically during the save operation. It works well.
However I have a test about the parsing of pages, and every-time it will download the file, which slows down the testing.
UPDATE:
I mount the uploader as the following (in the pre-existing paperclip column)
mount_uploader :image, TopicImageUploader, :mount_on => :image_file_name
I tried to stub the following, but neither worked:
Topic.any_instance.stub(:store_image!)
Topic.any_instance.stub(:store_image_file_name!)
Topic.any_instance.stub(:store_image_remote_url!)
TopicImageUploader.any_instance.stub(:download!)
This is what I'm using in my spec_helper:
class CarrierWave::Mount::Mounter
def store!
end
end
This completely blocks all real file uploads (note that I'm using this with carrier wave 0.5.8, which is newest version at the time of writing, if you're using much older version, it might differ). If you want to control tests which stub uploads, you could use:
CarrierWave::Mount::Mounter.any_instance.stub(:store!)
I reduced my test-suite time from 25 seconds to just 2 seconds with a simple config in the CarrierWave initializer:
# config/initializers/carrier_wave.rb
CarrierWave.configure do |config|
config.enable_processing = false if Rails.env.test?
end
This config skips the image manipulation (resizing, cropping, ...) of ImageMagick, MiniMagick ect.
allow_any_instance_of(CarrierWave::Uploader::Base).to receive(:store!).and_return nil

Resources