I was wondering whether it is possible to find out at runtime (i.e. in spec_helper.rb) what kind of spec is being executed (i.e. request, functional etc)?
Many Thanks!
If you're looking to add something like a before filter for only a certain type of spec, consider writing:
RSpec.configure do |config|
config.before(:each, type: :request) do
# request-spec only before hook
end
end
If you're trying to do something more complicated, you can access the current example's type via example.metadata[:type].
The way the rspec/rails project makes this distinction is by checking the file path.
For instance, a request spec will have a file path that matches /spec\/request/.
Related
I a have some code I'd like to refactor out of my step definitions and put them inside.. helpers?
Oh and please also say how to include them, I am really having a hard time finding any solid info on that.
Straight from the rspec documentation here: https://www.relishapp.com/rspec/rspec-core/docs/helper-methods/define-helper-methods-in-a-module#include-a-module-in-all-example-groups
Include a module in all example groups
Given a file named "include_module_spec.rb" with:
require './helpers'
RSpec.configure do |c|
c.include Helpers
end
RSpec.describe "an example group" do
it "has access to the helper methods defined in the module" do
expect(help).to be(:available)
end
end
When
I run rspec include_module_spec.rb
Then
the examples should all pass
You may also benefit from a a support/helpers folder Or equivalent which is covered pretty well here: How to include Rails Helpers on RSpec
I have a large model that takes time to initialize in my RSpec tests.
I want it potentially available to every example, but want to only load it if an example requires it.
This seems like the perfect use for let()'s lazy loading - only load it when you need it.
In any particular spec file I can do
require "spec_helper"
feature "foo" do
let(:big_class) { MyBigClass.new(bar) }
...
end
This will make big_class available to every example in that spec file.
Is there a way to make this more global so that EVERY spec file and example can use it? I couldn't find a good way to initialize let inside the spec helper.
You may simply define a shared context and include it in every example. Regarding your particular question, it should look like following:
RSpec.shared_context "Global helpers" do
let(:big_class) { MyBigClass.new(bar) }
end
RSpec.configure do |config|
config.include_context "Global helpers"
end
However, it's rarely a good idea to include a shared context in all examples, and that big_class helper from your question really looks like something domain-specific. You can steer the shared context inclusion by metadata, for example when you want to include given shared context in feature specs only (they all have :type => :feature metadata set by default), you can do it this way:
RSpec.configure do |config|
config.include_context "Feature spec helpers", :type => :feature
end
You might consider other approaches:
Use mock objects instead of real ones.
Refactor the initializer and extract the slow operation to another method
Mock objects of course bring their own set of drawbacks; they can become stale and make tests more brittle. But for some tests that is not an issue.
Refactoring initializers is a favorite of mine. E.g.
MyBigObject.new(args)
becomes
MyBigObject.new(args).setup
or :load_data or :connect_to_database_on_the_moon or whatever is taking a long time. You get the picture.
Obviously this means changing your code, but I find that often works out to be helpful in other ways, and it certainly makes testing easier.
You don't want to use let. From the docs:
Use let to define a memoized helper method. The value will be cached across
multiple calls in the same example but not across examples.
You'll end up instantiating MyBigClass lots of times. I would recommend creating a global helper method somewhere in spec_helper.rb (or similar) that used memo-ization on it's own to return the cached value if it's already been setup.
Also be very careful with all this as you're violating the rule of isolated tests. Might be fine for what you're doing, but it's a red flag.
Use global before hook with an instance variable
From the docs:
RSpec.configure do |c|
c.before(:example) { #big_class = MyBigClass.new(bar) }
end
RSpec.describe MyExample do
it { expect(#big_class).to eql(MyBigClass.new(bar)) } # This code will pass
end
For more details check the suggestions in this answer
I am building an API with Rails, using the rails-api gem. I want to use cucumber-rails and the gem 'Airborne' to test it.
Airborne comes with some nice helper methods for testing API responses, which I want to have access to in my step definitions. I have done this kind of thing before in Sinatra, which was relatively straightforward to configure in the /features/env.rb file.
It seems, however, that with rails-cucumber the creation of the 'World' happens behind the scenes somewhere and I don't know how to extend it to use the Airborne module after it's been created.
I have tried the following:
Airborne.configure do |config|
config.rack_app = Rails.application
end
Cucumber::Rails::World.extend(Airborne)
When(/^I make a request for information about an event$/) do
get "/events/1"
end
Then(/^I receive the information as a JSON$/) do
expect_json {}
end
I am still getting a NoMethodError on #expect_json, which is an Airborne method.
So my question is: how can I extend the instance of World in the context of cucumber-rails?
Don't panic, the World has been saved. The solution is to wrap Airborne and whatever else in a module:
module MyHelpers
include Airborne
include Capybara::DSL
end
Then pass that:
World(MyHelpers)
I am currently testing a Rails(4.2) helper with Rspec(3) successfully. However, the test file setup is a bit cumbersome. How can I streamline the require and/or include lines?
# spec/helpers/nav_helper_spec.rb
require 'spec_helper'
require_relative '../../app/helpers/nav_helper' # this seems bulky
describe NavHelper do
include NavHelper # this seems repetitive
...
end
Thanks in advance!
If you have a "default" setup you probably have a rails_helper in addition to your spec_helper. If you don't mind loading all of the Rails directories in this one spec (a bit of a performance hit) you can require that instead of the spec_helper (cleaning up the requires). But there's nothing wrong with including only what you need, it will run faster.
Rspec will also mix in the helper for you if it knows the spec type. You can either include config.infer_spec_type_from_file_location! in your spec helper, or include the type in the describe declaration:
describe NavHelper, type: :helper do
Either way you'll be able to use something like expect(helper.method_name).to eq(result) without explicitly including the module.
We want to validate after every request that there is no escaped HTML or XSS on the page. In Cucumber, we have an AfterStep that does this (as long as the current page is not the same as the previous page).
Is there any way to do this?
EDIT: See https://gist.github.com/603295 for an example of an old (no longer working) version of what I'm hoping to find.
Rspec gives you these after hooks:
https://www.relishapp.com/rspec/rspec-core/v/2-0/docs/hooks/before-and-after-hooks
In short:
after(:each)
after(:all)
after(:suite)
This is the best I could come up with, which was pretty close (however, it's still only after every example, not after every request):
In my spec helper, I added something like this:
RSpec.configure do |config|
config.after(:each, type: :feature) do
# this is run after each example in Capybara, and I did stuff like
page.body.should_not match(UNESCAPED_REGEX)
end
end