Any flag to skip using vcr for integration tests using guard-rspec? - guard

The vcr gem is awesome for speeding up a test suite that calls an external API...
What is the best procedure to check that the test suite would still pass if VCR is not used? Is there any default way to pass an environment variable or flag to skip VCR, using guard-rspec?
Or should I just wrap the VCR init block in a conditional checking an environment variable?
# spec/support/vcr.rb
require 'vcr'
unless ENV['SKIP_VCR'] && ENV['SKIP_VCR'].casecmp('YES') == 0
puts "Using VCR to speed up test suite!"
VCR.configure do |c|
c.cassette_library_dir = 'spec/cassettes'
c.hook_into :fakeweb
c.ignore_localhost = true
c.configure_rspec_metadata!
end
end

Related

Testing Elixir/Phoenix Service modules

I've been playing around with Elixir/Phoenix third-party modules. ( Modules that are used to fetch some data from a 3rd party service ) One of those module looking like so:
module TwitterService do
#twitter_url "https://api.twitter.com/1.1"
def fetch_tweets(user) do
# The actual code to fetch tweets
HTTPoison.get(#twitter_url)
|> process_response
end
def process_response({:ok, resp}) do
{:ok, Poison.decode! resp}
end
def process_response(_fail), do: {:ok, []}
end
The actual data doesn't matter in my question. So now, I'm interested in how can I dynamically configure the #twitter_url module variable in tests to make some of the tests fail on purpose. For example:
module TwitterServiceTest
test "Module returns {:ok, []} when Twitter API isn't available"
# I'd like this to be possible ( coming from the world of Rails )
TwitterService.configure(:twitter_url, "new_value") # This line isn't possible
# Now the TwiterService shouldn't get anything from the url
tweets = TwitterService.fetch_tweets("test")
assert {:ok, []} = tweets
end
end
How can I achieve this?
Note: I know I can use :configs to configure #twiter_url separately in dev and test environments, but I'd like to be able to test on a real response from the Twitter API too, and that would change the URL on the entire Test environment.
One of the solutions that I came up with was
def fetch_tweets(user, opts \\ []) do
_fetch_tweets(user, opts[:fail_on_test] || false)
end
defp _fetch_tweets(user, [fail_on_test: true]) do
# Fails
end
defp _fetch_tweets(user, [fail_on_test: false]) do
# Normal fetching
end
But that just seems hackish and silly, there must be a better solution to this.
As it was suggested by José in Mocks And Explicit Contracts, the best way would be probably to use a dependency injection:
module TwitterService do
#twitter_url "https://api.twitter.com/1.1"
def fetch_tweets(user, service_url \\ #twitter_url) do
# The actual code to fetch tweets
service_url
|> HTTPoison.get()
|> process_response
end
...
end
Now in tests you just inject another dependency when necessary:
# to test against real service
fetch_tweets(user)
# to test against mocked service
fetch_tweets(user, SOME_MOCK_URL)
This approach will also make it easier to plug in different service in the future. The processor implementation should not depend on it’s underlying service, assuming the service follows some contract (responds with json given a url in such a particular case.)
config sounds like a good way here. You can modify the value in the config at runtime in your test and then restore it after the test.
First, in your actual code, instead of #twitter_url, use Application.get_env(:my_app, :twitter_url).
Then, in your tests, you can use a wrapper function like this:
def with_twitter_url(new_twitter_url, func) do
old_twitter_url = Application.get_env(:my_app, :twitter_url)
Application.set_env(:my_app, :twitter_url, new_twitter_url)
func.()
Application.set_env(:my_app, :twitter_url, old_twitter_url)
end
Now in your tests, do:
with_twitter_url "<new url>", fn ->
# All calls to your module here will use the new url.
end
Make sure you're not using async tests for this as this technique modifies global environment.

Rails 3.2.5 in `eval': wrong number of arguments (0 for 2..3) (ArgumentError)

I wrote a runner (saved in lib folder). When starting the runner with: rails runner lib/test.rb
def aaa
puts "aaa"
end
aaa
it dumps:
in `eval': wrong number of arguments (0 for 2..3) (ArgumentError)
why?
rails runner is intended to run code from your app codebase as in
(from the guide)
rails runner "Model.long_running_method" # parses the string and executes
# the (hypothetical) method [long_running_method]
# from (hypothetical) model [app/models/model.rb]
the error raises from the fact that in your call you don't provide a string to evaluate
anyway to make it work this way (with a function from lib) you should
enclose your method in some class and
make the class available requiring it someway during application boot
! pay attention: if you call rails runner 'MyClass.my_method' you're calling a class method which has to be defined properly
def self.my_method
# your code
end
if you want to call an instance method you need to do rails runner 'MyClass.new.my_method'
All that said, rails runner boots all the rails app.
If that is not required, may I suggest to investigate whether a rake task could be suited for your needs ?

Capybara Acceptance DSL with MiniTest::Spec?

The readme for Capybara (see Using Capybara with MiniTest::Spec) says that I can do this if I include the module correctly, but it doesn't give any illustrative examples of how... I've tried including the module like this:
class MiniTest::Spec
include Capybara::DSL
end
... to no avail. I keep getting this error:
<main>': undefined methodfeature' for main:Object (NoMethodError)
How can I get it to work as it's written in the commented-out code?
spec/acceptance/api/reward_terms_spec.rb:
require "#{Dir.pwd}/spec/acceptance/acceptance_helper"
# this syntax works...
describe 'reward terms acceptance test' do
include Capybara::DSL
describe '#index' do
specify {
visit '/reward_terms'
# ...
}
end
end
# this syntax doesn't work...
# feature 'RewardTerms', %q{
# In order to get all reward terms available to me
# As an API client
# I want to list all active RewardTerms
# } do
# background do
# set_api_headers
# end
# scenario 'RewardTerm index' do
# visit '/reward_terms'
# ...
# end
# end
spec/acceptance/acceptance_helper.rb:
ENV["RAILS_ENV"] = "test"
require "#{Dir.pwd}/config/environment"
require 'minitest/autorun'
require 'capybara/rails'
def set_api_headers(device_id = 'abcd1234')
header 'Accept', 'application/json'
header 'X-Device-Id', device_id
end
There is a nice description in this post for how you should make MinitTest::Spec run with capybara. There he basically includes the Capybara::DSL into the base class of all the specs as in
class RequestSpec < MiniTest::Spec
include Rails.application.routes.url_helpers
include Capybara::DSL
end
this works rather nicely in our setup, but of course it does not reopen the MiniTest::Spec.
Here's a simple test_helper rig to run functional & integration tests in Rails using spec syntax. Based on a gist by tenderlove, the article mentioned above re: MiniTest with Capybara, as well as a lot of tinkering & source-poring.
https://gist.github.com/1607879
You should add minitest-rails-capybara gem to the Gemfile and add a word "feature" to the end of description as follows:
feature 'RewardTerms feature', %q{
In order to get all reward terms available to me
As an API client
I want to list all active RewardTerms
} do
background do
set_api_headers
end
scenario 'RewardTerm index' do
visit '/reward_terms'
#...
end
end
The special word "feature" is case incensitive, and can be "browser". You can customize it by adding a line to the test_helper.rb:
MiniTest::Spec.register_spec_type(/FooBar\z/i, Capybara::Rails::TestCase)

How to know which rails environment is running in delayed job

I have a delayed job which is working perfectly fine. I want to know which environment is this. Is it production or development or staging. Check the code please. ENV["RAILS_ENV"] is nuil?
class SendMessageJob < Struct.new(:message_id)
def perform
p ENV["RAILS_ENV"] // printing nil :(
p "hello world"
end
end
I tried including
require "#{File.dirname(__FILE__)}/../config/environment.rb"
on the top the file still nothing..
You can try RAILS_ENV (constant defined within Rails) or the newer one Rails.env (a wrapper for this).

How can I minimize logging of monitoring requests?

My rails app is pinged every minute for a health check and I want to keep these out of the log unless there is an error. I was able to do this in Rails 2.3.5 by setting the logger with this in application_controller.rb:
def logger
if params[:__no_logging__] == 'true' && params[:controller] == 'welcome'
&& params[:action] == 'index'
# ignore monitoring requests
RAILS_MONITOR_NULL_LOGGER
else
RAILS_DEFAULT_LOGGER
end
end
But this doesn't work in Rails 3.0.5
I've been able to put together a new solution by monkeypatching before_dispatch and after_dispatch in Rails::Rack::Dispatch:
require 'active_support/core_ext/time/conversions'
module Rails
module Rack
# Log the request started and flush all loggers after it.
class Logger
include ActiveSupport::BufferedLogger::Severity
def before_dispatch(env)
request = ActionDispatch::Request.new(env)
#path = request.filtered_path
path = request.fullpath
if request.path == '/' && request.parameters['__no_logging__'] == 'true'
#log_level = logger.level
logger.level = Logger::ERROR
#logger.level = 3
end
info
"\n\nStarted #{request.request_method}
\"#{path}\" " \
"for #{request.ip} at #{Time.now.to_default_s}"
end
def after_dispatch(env)
logger.level = #log_level unless #log_level.nil?
ActiveSupport::LogSubscriber.flush_all!
end
end
end
end
I put the patch in config/initializers/monkey_patch.rb
This works exactly as I need, I don't see this request in the log:
http://mydomain.com?__no_logging__=true
But all other request remain in the log unaffected
But there are still two problems:
1. I needed to comment out:
path = request.filtered_path
Because it causes this error:
ERROR NoMethodError: undefined method `filtered_path' for #<ActionDispatch::Request:0x105b4c0e8>
/ce_development/Rails/g3/config/initializers/monkey_patches.rb:52:in `before_dispatch'
/ce_development/Rails/g3/.bundle/ruby/1.8/gems/railties-3.0.5/lib/rails/rack/logger.rb:12:in `call'
...
I now understand this is not a problem. The offending method "request.filtered_path" doesn't exist in Rails 3.0.5, which I am using. I inadvertently copied my class from Rails 3.1.0.beta which does define filtered_path. Rails 3.0.5 uses request.fullpath as now shown above.
2. I needed to comment out
logger.level = Logger::ERROR
Because it causes this error:
ERROR NameError: uninitialized constant Rails::Rack::Logger::ERROR
/ce_development/Rails/g3/config/initializers/monkey_patches.rb:57:in `before_dispatch'
/ce_development/Rails/g3/.bundle/ruby/1.8/gems/railties-3.0.5/lib/rails/rack/logger.rb:12:in `call'
...
I solved this second problem by adding this line above
include ActiveSupport::BufferedLogger::Severity
I'm new to monkey patching and I can't figure out how to get filtered_path or Logger::Error defined in my patch. I've tried other requires, but no luck yet.
I'd also like any advice about the robustness of using this monkey patch on my project. Is there a better way to do this?
I know some people don't believe in altering logs, but I don't want all these pings in the log unless there is an error during its request.
A possible solution for Rails 3 where the Logger is replaced by a Custom Logger is described here: Silencing the Rails log on a per-action basis and here: How can I disable logging in Ruby on Rails on a per-action basis?. I had to add require 'rails/all' to the custom_logger.rb class to make it work.